provider_test.go 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  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. custom := &models.CustomPricing{}
  402. for _, c := range cases {
  403. t.Run(c.name, func(t *testing.T) {
  404. pricingObj, err := processDescribePriceAndCreateAlibabaPricing(client, c.teststruct, signer, custom)
  405. if err != nil && c.expectedError == nil {
  406. t.Fatalf("Case name %s: got an error %s", c.name, err)
  407. }
  408. if c.teststruct != nil {
  409. if pricingObj == nil {
  410. t.Fatalf("Case name %s: got a nil pricing object", c.name)
  411. }
  412. t.Logf("Case name %s: Pricing Information gathered for instanceType is %v", c.name, pricingObj.PricingTerms.PricingDetails.TradePrice)
  413. }
  414. })
  415. }
  416. }
  417. func TestGetInstanceFamilyFromType(t *testing.T) {
  418. cases := []struct {
  419. name string
  420. instanceType string
  421. expectedInstanceFamily string
  422. }{
  423. {
  424. name: "test if ecs.[instance-family].[different-type] work",
  425. instanceType: "ecs.sn2ne.2xlarge",
  426. expectedInstanceFamily: "sn2ne",
  427. },
  428. {
  429. name: "test if random word gives you ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE value ",
  430. instanceType: "random.value",
  431. expectedInstanceFamily: ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE,
  432. },
  433. {
  434. name: "test if random instance family gives you ALIBABA_NOT_SUPPORTED_INSTANCE_FAMILY_TYPE value ",
  435. instanceType: "ecs.g7e.2xlarge",
  436. expectedInstanceFamily: ALIBABA_NOT_SUPPORTED_INSTANCE_FAMILY_TYPE,
  437. },
  438. }
  439. for _, c := range cases {
  440. t.Run(c.name, func(t *testing.T) {
  441. returnValue := getInstanceFamilyFromType(c.instanceType)
  442. if returnValue != c.expectedInstanceFamily {
  443. t.Fatalf("Case name %s: expected instance family of type %s but got %s", c.name, c.expectedInstanceFamily, returnValue)
  444. }
  445. })
  446. }
  447. }
  448. func TestDetermineKeyForPricing(t *testing.T) {
  449. type randomK8sStruct struct {
  450. name string
  451. }
  452. cases := []struct {
  453. name string
  454. testVar interface{}
  455. expectedKey string
  456. expectedError error
  457. }{
  458. {
  459. name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used in Node key",
  460. testVar: &SlimK8sNode{
  461. InstanceType: "ecs.sn2.large",
  462. RegionID: "cn-hangzhou",
  463. PriceUnit: "Hour",
  464. MemorySizeInKiB: "16777216KiB",
  465. IsIoOptimized: true,
  466. OSType: "linux",
  467. ProviderID: "cn-hangzhou.i-test-04",
  468. InstanceTypeFamily: "sn2",
  469. },
  470. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize",
  471. expectedError: nil,
  472. },
  473. {
  474. name: "test missing InstanceType to create Node key",
  475. testVar: &SlimK8sNode{
  476. RegionID: "cn-hangzhou",
  477. PriceUnit: "Hour",
  478. MemorySizeInKiB: "16777216KiB",
  479. IsIoOptimized: true,
  480. OSType: "linux",
  481. ProviderID: "cn-hangzhou.i-test-04",
  482. },
  483. expectedKey: "cn-hangzhou::linux::optimize",
  484. expectedError: nil,
  485. },
  486. {
  487. name: "test when node has a systemDisk Information with missing Performance level",
  488. testVar: &SlimK8sNode{
  489. InstanceType: "ecs.sn2.large",
  490. RegionID: "cn-hangzhou",
  491. PriceUnit: "Hour",
  492. MemorySizeInKiB: "16777216KiB",
  493. IsIoOptimized: true,
  494. OSType: "linux",
  495. ProviderID: "cn-hangzhou.i-test-04",
  496. InstanceTypeFamily: "sn2",
  497. SystemDisk: &SlimK8sDisk{
  498. DiskType: "system",
  499. RegionID: "cn-hangzhou",
  500. PriceUnit: "Hour",
  501. SizeInGiB: "40",
  502. DiskCategory: "cloud_efficiency",
  503. ProviderID: "d-Ali-cloud-XXX-i1",
  504. StorageClass: "",
  505. },
  506. },
  507. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize::cloud_efficiency::40",
  508. expectedError: nil,
  509. },
  510. {
  511. name: "test when node has a systemDisk Information with all information",
  512. testVar: &SlimK8sNode{
  513. InstanceType: "ecs.sn2.large",
  514. RegionID: "cn-hangzhou",
  515. PriceUnit: "Hour",
  516. MemorySizeInKiB: "16777216KiB",
  517. IsIoOptimized: true,
  518. OSType: "linux",
  519. ProviderID: "cn-hangzhou.i-test-04",
  520. InstanceTypeFamily: "sn2",
  521. SystemDisk: &SlimK8sDisk{
  522. DiskType: "data",
  523. RegionID: "cn-hangzhou",
  524. PriceUnit: "Hour",
  525. SizeInGiB: "80",
  526. DiskCategory: "cloud_ssd",
  527. PerformanceLevel: "PL2",
  528. ProviderID: "d-Ali-cloud-XXX-04",
  529. StorageClass: "",
  530. },
  531. },
  532. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize::cloud_ssd::80::PL2",
  533. expectedError: nil,
  534. },
  535. {
  536. name: "test random k8s struct should return unsupported error",
  537. testVar: &randomK8sStruct{
  538. name: "test struct",
  539. },
  540. expectedKey: "",
  541. expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
  542. },
  543. {
  544. name: "test for nil check",
  545. testVar: nil,
  546. expectedKey: "",
  547. expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
  548. },
  549. {
  550. name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used to key",
  551. testVar: &SlimK8sDisk{
  552. DiskType: "data",
  553. RegionID: "cn-hangzhou",
  554. PriceUnit: "Hour",
  555. SizeInGiB: "40",
  556. DiskCategory: "cloud_efficiency",
  557. ProviderID: "d-Ali-cloud-XXX-02",
  558. StorageClass: "temp",
  559. },
  560. expectedKey: "cn-hangzhou::data::cloud_efficiency::40",
  561. expectedError: nil,
  562. },
  563. {
  564. name: "test missing InstanceType to create key",
  565. testVar: &SlimK8sDisk{
  566. DiskType: "data",
  567. RegionID: "cn-hangzhou",
  568. PriceUnit: "Hour",
  569. SizeInGiB: "80",
  570. DiskCategory: "cloud_ssd",
  571. PerformanceLevel: "PL2",
  572. ProviderID: "d-Ali-cloud-XXX-04",
  573. StorageClass: "temp",
  574. },
  575. expectedKey: "cn-hangzhou::data::cloud_ssd::PL2::80",
  576. expectedError: nil,
  577. },
  578. }
  579. for _, c := range cases {
  580. t.Run(c.name, func(t *testing.T) {
  581. returnString, returnErr := determineKeyForPricing(c.testVar)
  582. if c.expectedError == nil && returnErr != nil {
  583. t.Fatalf("Case name %s: expected error was nil but received error %v", c.name, returnErr)
  584. }
  585. if returnString != c.expectedKey {
  586. t.Fatalf("Case name %s: determineKeyForPricing received %s but expected %s", c.name, returnString, c.expectedKey)
  587. }
  588. })
  589. }
  590. }
  591. func TestGenerateSlimK8sNodeFromV1Node(t *testing.T) {
  592. testv1Node := &v1.Node{}
  593. testv1Node.Labels = make(map[string]string)
  594. testv1Node.Labels["topology.kubernetes.io/region"] = "us-east-1"
  595. testv1Node.Labels["beta.kubernetes.io/os"] = "linux"
  596. testv1Node.Labels["node.kubernetes.io/instance-type"] = "ecs.sn2ne.2xlarge"
  597. testv1Node.Status.Capacity = v1.ResourceList{
  598. v1.ResourceMemory: *resource.NewQuantity(16, resource.BinarySI),
  599. }
  600. cases := []struct {
  601. name string
  602. testNode *v1.Node
  603. expectedSlimNode *SlimK8sNode
  604. }{
  605. {
  606. name: "test a generic *v1.Node to *SlimK8sNode Conversion",
  607. testNode: testv1Node,
  608. expectedSlimNode: &SlimK8sNode{
  609. InstanceType: "ecs.sn2ne.2xlarge",
  610. RegionID: "us-east-1",
  611. PriceUnit: ALIBABA_HOUR_PRICE_UNIT,
  612. MemorySizeInKiB: "16",
  613. IsIoOptimized: true,
  614. OSType: "linux",
  615. InstanceTypeFamily: "sn2ne",
  616. },
  617. },
  618. }
  619. for _, c := range cases {
  620. t.Run(c.name, func(t *testing.T) {
  621. returnSlimK8sNode := generateSlimK8sNodeFromV1Node(c.testNode)
  622. if returnSlimK8sNode.InstanceType != c.expectedSlimNode.InstanceType {
  623. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceType: %s , received InstanceType: %s", c.expectedSlimNode.InstanceType, returnSlimK8sNode.InstanceType)
  624. }
  625. if returnSlimK8sNode.RegionID != c.expectedSlimNode.RegionID {
  626. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected RegionID: %s , received RegionID: %s", c.expectedSlimNode.RegionID, returnSlimK8sNode.RegionID)
  627. }
  628. if returnSlimK8sNode.PriceUnit != c.expectedSlimNode.PriceUnit {
  629. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected PriceUnit: %s , received PriceUnit: %s", c.expectedSlimNode.PriceUnit, returnSlimK8sNode.PriceUnit)
  630. }
  631. if returnSlimK8sNode.MemorySizeInKiB != c.expectedSlimNode.MemorySizeInKiB {
  632. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected MemorySizeInKiB: %s , received MemorySizeInKiB: %s", c.expectedSlimNode.MemorySizeInKiB, returnSlimK8sNode.MemorySizeInKiB)
  633. }
  634. if returnSlimK8sNode.OSType != c.expectedSlimNode.OSType {
  635. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected OSType: %s , received OSType: %s", c.expectedSlimNode.OSType, returnSlimK8sNode.OSType)
  636. }
  637. if returnSlimK8sNode.InstanceTypeFamily != c.expectedSlimNode.InstanceTypeFamily {
  638. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceTypeFamily: %s , received InstanceTypeFamily: %s", c.expectedSlimNode.InstanceTypeFamily, returnSlimK8sNode.InstanceTypeFamily)
  639. }
  640. })
  641. }
  642. }
  643. func TestGenerateSlimK8sDiskFromV1PV(t *testing.T) {
  644. testv1PV := &v1.PersistentVolume{}
  645. testv1PV.Spec.Capacity = v1.ResourceList{
  646. v1.ResourceStorage: *resource.NewQuantity(16*1024*1024*1024, resource.BinarySI),
  647. }
  648. testv1PV.Spec.CSI = &v1.CSIPersistentVolumeSource{}
  649. testv1PV.Spec.CSI.VolumeHandle = "testPV"
  650. testv1PV.Spec.CSI.VolumeAttributes = map[string]string{
  651. "performanceLevel": "PL2",
  652. "type": "cloud_essd",
  653. }
  654. testv1PV.Spec.CSI.VolumeHandle = "testPV"
  655. testv1PV.Spec.StorageClassName = "testStorageClass"
  656. cases := []struct {
  657. name string
  658. testPV *v1.PersistentVolume
  659. expectedSlimDisk *SlimK8sDisk
  660. inpRegionID string
  661. }{
  662. {
  663. name: "test a generic *v1.Node to *SlimK8sNode Conversion",
  664. testPV: testv1PV,
  665. expectedSlimDisk: &SlimK8sDisk{
  666. DiskType: ALIBABA_DATA_DISK_CATEGORY,
  667. RegionID: "us-east-1",
  668. PriceUnit: ALIBABA_HOUR_PRICE_UNIT,
  669. SizeInGiB: "16",
  670. DiskCategory: "cloud_essd",
  671. PerformanceLevel: "PL2",
  672. ProviderID: "testPV",
  673. StorageClass: "testStorageClass",
  674. },
  675. inpRegionID: "us-east-1",
  676. },
  677. }
  678. for _, c := range cases {
  679. t.Run(c.name, func(t *testing.T) {
  680. returnSlimK8sDisk := generateSlimK8sDiskFromV1PV(c.testPV, c.inpRegionID)
  681. if returnSlimK8sDisk.DiskType != c.expectedSlimDisk.DiskType {
  682. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected DiskType: %s , received DiskType: %s", c.expectedSlimDisk.DiskType, returnSlimK8sDisk.DiskType)
  683. }
  684. if returnSlimK8sDisk.RegionID != c.expectedSlimDisk.RegionID {
  685. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected RegionID: %s , received RegionID Type: %s", c.expectedSlimDisk.RegionID, returnSlimK8sDisk.RegionID)
  686. }
  687. if returnSlimK8sDisk.PriceUnit != c.expectedSlimDisk.PriceUnit {
  688. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected PriceUnit: %s , received PriceUnit Type: %s", c.expectedSlimDisk.PriceUnit, returnSlimK8sDisk.PriceUnit)
  689. }
  690. if returnSlimK8sDisk.SizeInGiB != c.expectedSlimDisk.SizeInGiB {
  691. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected SizeInGiB: %s , received SizeInGiB Type: %s", c.expectedSlimDisk.SizeInGiB, returnSlimK8sDisk.SizeInGiB)
  692. }
  693. if returnSlimK8sDisk.DiskCategory != c.expectedSlimDisk.DiskCategory {
  694. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected DiskCategory: %s , received DiskCategory Type: %s", c.expectedSlimDisk.DiskCategory, returnSlimK8sDisk.DiskCategory)
  695. }
  696. if returnSlimK8sDisk.PerformanceLevel != c.expectedSlimDisk.PerformanceLevel {
  697. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected PerformanceLevel: %s , received PerformanceLevel Type: %s", c.expectedSlimDisk.PerformanceLevel, returnSlimK8sDisk.PerformanceLevel)
  698. }
  699. if returnSlimK8sDisk.ProviderID != c.expectedSlimDisk.ProviderID {
  700. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected ProviderID: %s , received ProviderID Type: %s", c.expectedSlimDisk.ProviderID, returnSlimK8sDisk.ProviderID)
  701. }
  702. if returnSlimK8sDisk.StorageClass != c.expectedSlimDisk.StorageClass {
  703. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected StorageClass: %s , received StorageClass Type: %s", c.expectedSlimDisk.StorageClass, returnSlimK8sDisk.StorageClass)
  704. }
  705. })
  706. }
  707. }
  708. func TestGetNumericalValueFromResourceQuantity(t *testing.T) {
  709. cases := []struct {
  710. name string
  711. inputResourceQuanity string
  712. expectedValue string
  713. }{
  714. {
  715. name: "positive scenario: when inputResourceQuantity is 10Gi",
  716. inputResourceQuanity: "10Gi",
  717. expectedValue: "10",
  718. },
  719. {
  720. name: "negative scenario: when inputResourceQuantity is Gi",
  721. inputResourceQuanity: "Gi",
  722. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  723. },
  724. {
  725. name: "negative scenario: when inputResourceQuantity is 10",
  726. inputResourceQuanity: "10",
  727. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  728. },
  729. {
  730. name: "negative scenario: when inputResourceQuantity is empty string",
  731. inputResourceQuanity: "",
  732. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  733. },
  734. }
  735. for _, c := range cases {
  736. t.Run(c.name, func(t *testing.T) {
  737. returnValue := getNumericalValueFromResourceQuantity(c.inputResourceQuanity)
  738. if c.expectedValue != returnValue {
  739. t.Fatalf("Case name %s: getNumericalValueFromResourceQuantity received %s but expected %s", c.name, returnValue, c.expectedValue)
  740. }
  741. })
  742. }
  743. }
  744. func TestDeterminePVRegion(t *testing.T) {
  745. genericNodeAffinityTestStruct := v1.NodeSelectorTerm{
  746. MatchExpressions: []v1.NodeSelectorRequirement{
  747. {
  748. Key: "topology.diskplugin.csi.alibabacloud.com/zone",
  749. Operator: v1.NodeSelectorOpIn,
  750. Values: []string{"us-east-1a"},
  751. },
  752. },
  753. MatchFields: []v1.NodeSelectorRequirement{},
  754. }
  755. // testPV1 contains the Label with region information as well as node affinity in spec
  756. testPV1 := &v1.PersistentVolume{}
  757. testPV1.Name = "testPV1"
  758. testPV1.Labels = make(map[string]string)
  759. testPV1.Labels[ALIBABA_DISK_TOPOLOGY_REGION_LABEL] = "us-east-1"
  760. testPV1.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
  761. Required: &v1.NodeSelector{
  762. NodeSelectorTerms: []v1.NodeSelectorTerm{genericNodeAffinityTestStruct},
  763. },
  764. }
  765. // testPV2 contains the only zone label
  766. testPV2 := &v1.PersistentVolume{}
  767. testPV2.Name = "testPV2"
  768. testPV2.Labels = make(map[string]string)
  769. testPV2.Labels[ALIBABA_DISK_TOPOLOGY_ZONE_LABEL] = "us-east-1a"
  770. // testPV3 contains only node affinity in spec
  771. testPV3 := &v1.PersistentVolume{}
  772. testPV3.Name = "testPV3"
  773. testPV3.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
  774. Required: &v1.NodeSelector{
  775. NodeSelectorTerms: []v1.NodeSelectorTerm{genericNodeAffinityTestStruct},
  776. },
  777. }
  778. // testPV4 contains no label/annotation or any node affinity
  779. testPV4 := &v1.PersistentVolume{}
  780. testPV4.Name = "testPV4"
  781. cases := []struct {
  782. name string
  783. inputPV *v1.PersistentVolume
  784. expectedRegion string
  785. }{
  786. {
  787. name: "When Region label topology.diskplugin.csi.alibabacloud.com/region is present along with node affinity details",
  788. inputPV: testPV1,
  789. expectedRegion: "us-east-1",
  790. },
  791. {
  792. name: "When zone label topology.diskplugin.csi.alibabacloud.com/zone is present function has to determine region",
  793. inputPV: testPV2,
  794. expectedRegion: "us-east-1",
  795. },
  796. {
  797. name: "When only node affinity detail is present function has to determine the region",
  798. inputPV: testPV3,
  799. expectedRegion: "us-east-1",
  800. },
  801. {
  802. name: "When no region/zone information is present function returns empty to default to cluster region",
  803. inputPV: testPV4,
  804. expectedRegion: "",
  805. },
  806. }
  807. for _, c := range cases {
  808. t.Run(c.name, func(t *testing.T) {
  809. returnRegion := determinePVRegion(c.inputPV)
  810. if c.expectedRegion != returnRegion {
  811. t.Fatalf("Case name %s: determinePVRegion received region :%s but expected region: %s", c.name, returnRegion, c.expectedRegion)
  812. }
  813. })
  814. }
  815. }