provider_test.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  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 General Purpose Type g8a instance family",
  106. teststruct: &SlimK8sNode{
  107. InstanceType: "ecs.g8a.8xlarge",
  108. RegionID: "cn-hangzhou",
  109. PriceUnit: "Hour",
  110. MemorySizeInKiB: "33554432KiB",
  111. IsIoOptimized: true,
  112. OSType: "Linux",
  113. ProviderID: "cn-hangzhou.i-test-01c",
  114. InstanceTypeFamily: "g8a",
  115. },
  116. expectedError: nil,
  117. },
  118. {
  119. name: "test Enhanced General Purpose Type g6e instance family",
  120. teststruct: &SlimK8sNode{
  121. InstanceType: "ecs.g6e.xlarge",
  122. RegionID: "cn-hangzhou",
  123. PriceUnit: "Hour",
  124. MemorySizeInKiB: "16777216KiB",
  125. IsIoOptimized: true,
  126. OSType: "Linux",
  127. ProviderID: "cn-hangzhou.i-test-01",
  128. InstanceTypeFamily: "g6e",
  129. },
  130. expectedError: nil,
  131. },
  132. {
  133. name: "test General Purpose Type g6 instance family",
  134. teststruct: &SlimK8sNode{
  135. InstanceType: "ecs.g6.3xlarge",
  136. RegionID: "cn-hangzhou",
  137. PriceUnit: "Hour",
  138. MemorySizeInKiB: "50331648KiB",
  139. IsIoOptimized: true,
  140. OSType: "Linux",
  141. ProviderID: "cn-hangzhou.i-test-02",
  142. InstanceTypeFamily: "g6",
  143. },
  144. expectedError: nil,
  145. },
  146. {
  147. name: "test General Purpose Type g5 instance family",
  148. teststruct: &SlimK8sNode{
  149. InstanceType: "ecs.g5.2xlarge",
  150. RegionID: "cn-hangzhou",
  151. PriceUnit: "Hour",
  152. MemorySizeInKiB: "33554432KiB",
  153. IsIoOptimized: true,
  154. OSType: "Linux",
  155. ProviderID: "cn-hangzhou.i-test-03",
  156. InstanceTypeFamily: "g5",
  157. },
  158. expectedError: nil,
  159. },
  160. {
  161. name: "test General Purpose Type sn2 instance family",
  162. teststruct: &SlimK8sNode{
  163. InstanceType: "ecs.sn2.large",
  164. RegionID: "cn-hangzhou",
  165. PriceUnit: "Hour",
  166. MemorySizeInKiB: "16777216KiB",
  167. IsIoOptimized: true,
  168. OSType: "Linux",
  169. ProviderID: "cn-hangzhou.i-test-04",
  170. InstanceTypeFamily: "sn2",
  171. },
  172. expectedError: nil,
  173. },
  174. {
  175. name: "test General Purpose Type with Enhanced Network Performance sn2ne instance family",
  176. teststruct: &SlimK8sNode{
  177. InstanceType: "ecs.sn2ne.2xlarge",
  178. RegionID: "cn-hangzhou",
  179. PriceUnit: "Hour",
  180. MemorySizeInKiB: "33554432KiB",
  181. IsIoOptimized: true,
  182. OSType: "Linux",
  183. ProviderID: "cn-hangzhou.i-test-05",
  184. InstanceTypeFamily: "sn2ne",
  185. },
  186. expectedError: nil,
  187. },
  188. {
  189. name: "test Memory Optmized instance type r7 instance family",
  190. teststruct: &SlimK8sNode{
  191. InstanceType: "ecs.r7.6xlarge",
  192. RegionID: "cn-hangzhou",
  193. PriceUnit: "Hour",
  194. MemorySizeInKiB: "2013265592KiB",
  195. IsIoOptimized: true,
  196. OSType: "Linux",
  197. ProviderID: "cn-hangzhou.i-test-06",
  198. InstanceTypeFamily: "r7",
  199. },
  200. expectedError: nil,
  201. },
  202. {
  203. name: "test Memory Optmized instance type r7a instance family",
  204. teststruct: &SlimK8sNode{
  205. InstanceType: "ecs.r7a.8xlarge",
  206. RegionID: "cn-hangzhou",
  207. PriceUnit: "Hour",
  208. MemorySizeInKiB: "33554432KiB",
  209. IsIoOptimized: true,
  210. OSType: "Linux",
  211. ProviderID: "cn-hangzhou.i-test-06a",
  212. InstanceTypeFamily: "r7a",
  213. },
  214. expectedError: nil,
  215. },
  216. {
  217. name: "test Enhanced Memory Optmized instance type r6e instance family",
  218. teststruct: &SlimK8sNode{
  219. InstanceType: "ecs.r6e.4xlarge",
  220. RegionID: "cn-hangzhou",
  221. PriceUnit: "Hour",
  222. MemorySizeInKiB: "2013265592KiB",
  223. IsIoOptimized: true,
  224. OSType: "Linux",
  225. ProviderID: "cn-hangzhou.i-test-07",
  226. InstanceTypeFamily: "r6e",
  227. },
  228. expectedError: nil,
  229. },
  230. {
  231. name: "test Memory Optmized instance type r6a instance family",
  232. teststruct: &SlimK8sNode{
  233. InstanceType: "ecs.r6a.8xlarge",
  234. RegionID: "cn-hangzhou",
  235. PriceUnit: "Hour",
  236. MemorySizeInKiB: "33554432KiB",
  237. IsIoOptimized: true,
  238. OSType: "Linux",
  239. ProviderID: "cn-hangzhou.i-test-07a",
  240. InstanceTypeFamily: "r6a",
  241. },
  242. expectedError: nil,
  243. },
  244. {
  245. name: "test Memory Optmized instance type r6 instance family",
  246. teststruct: &SlimK8sNode{
  247. InstanceType: "ecs.r6.8xlarge",
  248. RegionID: "cn-hangzhou",
  249. PriceUnit: "Hour",
  250. MemorySizeInKiB: "33554432KiB",
  251. IsIoOptimized: true,
  252. OSType: "Linux",
  253. ProviderID: "cn-hangzhou.i-test-08",
  254. InstanceTypeFamily: "r6",
  255. },
  256. expectedError: nil,
  257. },
  258. {
  259. name: "test Memory type instance and r5 instance family",
  260. teststruct: &SlimK8sNode{
  261. InstanceType: "ecs.r5.xlarge",
  262. RegionID: "cn-hangzhou",
  263. PriceUnit: "Hour",
  264. MemorySizeInKiB: "33554432KiB",
  265. IsIoOptimized: true,
  266. OSType: "Linux",
  267. ProviderID: "cn-hangzhou.i-test-09",
  268. InstanceTypeFamily: "r5",
  269. },
  270. expectedError: nil,
  271. },
  272. {
  273. name: "test Memory Optmized instance type with se1 instance family",
  274. teststruct: &SlimK8sNode{
  275. InstanceType: "ecs.se1.4xlarge",
  276. RegionID: "cn-hangzhou",
  277. PriceUnit: "Hour",
  278. MemorySizeInKiB: "16777216KiB",
  279. IsIoOptimized: true,
  280. OSType: "Linux",
  281. ProviderID: "cn-hangzhou.i-test-10",
  282. InstanceTypeFamily: "se1",
  283. },
  284. expectedError: nil,
  285. },
  286. {
  287. name: "test Memory Optmized instance type with Enhanced Network Performance se1ne instance family",
  288. teststruct: &SlimK8sNode{
  289. InstanceType: "ecs.se1ne.3xlarge",
  290. RegionID: "cn-hangzhou",
  291. PriceUnit: "Hour",
  292. MemorySizeInKiB: "100663296KiB",
  293. IsIoOptimized: true,
  294. OSType: "Linux",
  295. ProviderID: "cn-hangzhou.i-test-11",
  296. InstanceTypeFamily: "se1ne",
  297. },
  298. expectedError: nil,
  299. },
  300. {
  301. name: "test High Memory type with re6 instance family",
  302. teststruct: &SlimK8sNode{
  303. InstanceType: "ecs.re6.8xlarge",
  304. RegionID: "cn-hangzhou",
  305. PriceUnit: "Hour",
  306. MemorySizeInKiB: "33554432KiB",
  307. IsIoOptimized: true,
  308. OSType: "Linux",
  309. ProviderID: "cn-hangzhou.i-test-12",
  310. InstanceTypeFamily: "re6",
  311. },
  312. expectedError: nil,
  313. },
  314. {
  315. name: "test Persistent Memory Optimized type with re6p instance family",
  316. teststruct: &SlimK8sNode{
  317. InstanceType: "ecs.re6p.4xlarge",
  318. RegionID: "cn-hangzhou",
  319. PriceUnit: "Hour",
  320. MemorySizeInKiB: "33554432KiB",
  321. IsIoOptimized: true,
  322. OSType: "Linux",
  323. ProviderID: "cn-hangzhou.i-test-13",
  324. InstanceTypeFamily: "re6p",
  325. },
  326. expectedError: nil,
  327. },
  328. {
  329. name: "test Memory type with re4 instance family",
  330. teststruct: &SlimK8sNode{
  331. InstanceType: "ecs.re4.10xlarge",
  332. RegionID: "cn-hangzhou",
  333. PriceUnit: "Hour",
  334. MemorySizeInKiB: "41943040KiB",
  335. IsIoOptimized: true,
  336. OSType: "Linux",
  337. ProviderID: "cn-hangzhou.i-test-14",
  338. InstanceTypeFamily: "re4",
  339. },
  340. expectedError: nil,
  341. },
  342. {
  343. name: "test Memory optimized type with se1 instance family",
  344. teststruct: &SlimK8sNode{
  345. InstanceType: "ecs.se1.8xlarge",
  346. RegionID: "cn-hangzhou",
  347. PriceUnit: "Hour",
  348. MemorySizeInKiB: "33554432KiB",
  349. IsIoOptimized: true,
  350. OSType: "Linux",
  351. ProviderID: "cn-hangzhou.i-test-15",
  352. InstanceTypeFamily: "se1",
  353. },
  354. expectedError: nil,
  355. },
  356. {
  357. name: "test for a nil information",
  358. teststruct: nil,
  359. expectedError: fmt.Errorf("unsupported ECS pricing component at this time"),
  360. },
  361. {
  362. name: "test Cloud Disk with Category cloud representing basic disk",
  363. teststruct: &SlimK8sDisk{
  364. DiskType: "data",
  365. RegionID: "cn-hangzhou",
  366. PriceUnit: "Hour",
  367. SizeInGiB: "20",
  368. DiskCategory: "cloud",
  369. ProviderID: "d-Ali-cloud-XXX-01",
  370. StorageClass: "temp",
  371. },
  372. expectedError: nil,
  373. },
  374. {
  375. name: "test Cloud Disk with Category cloud_efficiency representing ultra disk",
  376. teststruct: &SlimK8sDisk{
  377. DiskType: "data",
  378. RegionID: "cn-hangzhou",
  379. PriceUnit: "Hour",
  380. SizeInGiB: "40",
  381. DiskCategory: "cloud_efficiency",
  382. ProviderID: "d-Ali-cloud-XXX-02",
  383. StorageClass: "temp",
  384. },
  385. expectedError: nil,
  386. },
  387. {
  388. name: "test Cloud Disk with Category cloud_ssd representing standard SSD",
  389. teststruct: &SlimK8sDisk{
  390. DiskType: "data",
  391. RegionID: "cn-hangzhou",
  392. PriceUnit: "Hour",
  393. SizeInGiB: "40",
  394. DiskCategory: "cloud_efficiency",
  395. ProviderID: "d-Ali-cloud-XXX-02",
  396. StorageClass: "temp",
  397. },
  398. expectedError: nil,
  399. },
  400. {
  401. name: "test Cloud Disk with Category cloud_essd representing Enhanced SSD with PL2 performance level",
  402. teststruct: &SlimK8sDisk{
  403. DiskType: "data",
  404. RegionID: "cn-hangzhou",
  405. PriceUnit: "Hour",
  406. SizeInGiB: "80",
  407. DiskCategory: "cloud_ssd",
  408. PerformanceLevel: "PL2",
  409. ProviderID: "d-Ali-cloud-XXX-04",
  410. StorageClass: "temp",
  411. },
  412. expectedError: nil,
  413. },
  414. {
  415. name: "test incorrect disk type",
  416. teststruct: &SlimK8sNode{
  417. InstanceType: "ecs.g6.xlarge",
  418. RegionID: "ap-northeast-1",
  419. PriceUnit: "Hour",
  420. MemorySizeInKiB: "33554432KiB",
  421. IsIoOptimized: true,
  422. OSType: "Linux",
  423. ProviderID: "cn-hangzhou.i-test-15",
  424. InstanceTypeFamily: "se1",
  425. SystemDisk: &SlimK8sDisk{
  426. DiskType: "data",
  427. RegionID: "ap-northeast-1",
  428. PriceUnit: "Hour",
  429. SizeInGiB: "40",
  430. DiskCategory: "cloud_essd",
  431. PerformanceLevel: "PL1",
  432. ProviderID: "d-Ali-cloud-XXX-04",
  433. StorageClass: "temp",
  434. },
  435. },
  436. expectedError: nil,
  437. },
  438. }
  439. custom := &models.CustomPricing{}
  440. for _, c := range cases {
  441. t.Run(c.name, func(t *testing.T) {
  442. pricingObj, err := processDescribePriceAndCreateAlibabaPricing(client, c.teststruct, signer, custom)
  443. if err != nil && c.expectedError == nil {
  444. t.Fatalf("Case name %s: got an error %s", c.name, err)
  445. }
  446. if c.teststruct != nil {
  447. if pricingObj == nil {
  448. t.Fatalf("Case name %s: got a nil pricing object", c.name)
  449. }
  450. t.Logf("Case name %s: Pricing Information gathered for instanceType is %v", c.name, pricingObj.PricingTerms.PricingDetails.TradePrice)
  451. }
  452. })
  453. }
  454. }
  455. func TestGetInstanceFamilyFromType(t *testing.T) {
  456. cases := []struct {
  457. name string
  458. instanceType string
  459. expectedInstanceFamily string
  460. }{
  461. {
  462. name: "test if ecs.[instance-family].[different-type] work",
  463. instanceType: "ecs.sn2ne.2xlarge",
  464. expectedInstanceFamily: "sn2ne",
  465. },
  466. {
  467. name: "test if random word gives you ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE value ",
  468. instanceType: "random.value",
  469. expectedInstanceFamily: ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE,
  470. },
  471. }
  472. for _, c := range cases {
  473. t.Run(c.name, func(t *testing.T) {
  474. returnValue := getInstanceFamilyFromType(c.instanceType)
  475. if returnValue != c.expectedInstanceFamily {
  476. t.Fatalf("Case name %s: expected instance family of type %s but got %s", c.name, c.expectedInstanceFamily, returnValue)
  477. }
  478. })
  479. }
  480. }
  481. func TestDetermineKeyForPricing(t *testing.T) {
  482. type randomK8sStruct struct {
  483. name string
  484. }
  485. cases := []struct {
  486. name string
  487. testVar interface{}
  488. expectedKey string
  489. expectedError error
  490. }{
  491. {
  492. name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used in Node key",
  493. testVar: &SlimK8sNode{
  494. InstanceType: "ecs.sn2.large",
  495. RegionID: "cn-hangzhou",
  496. PriceUnit: "Hour",
  497. MemorySizeInKiB: "16777216KiB",
  498. IsIoOptimized: true,
  499. OSType: "linux",
  500. ProviderID: "cn-hangzhou.i-test-04",
  501. InstanceTypeFamily: "sn2",
  502. },
  503. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize",
  504. expectedError: nil,
  505. },
  506. {
  507. name: "test missing InstanceType to create Node key",
  508. testVar: &SlimK8sNode{
  509. RegionID: "cn-hangzhou",
  510. PriceUnit: "Hour",
  511. MemorySizeInKiB: "16777216KiB",
  512. IsIoOptimized: true,
  513. OSType: "linux",
  514. ProviderID: "cn-hangzhou.i-test-04",
  515. },
  516. expectedKey: "cn-hangzhou::linux::optimize",
  517. expectedError: nil,
  518. },
  519. {
  520. name: "test when node has a systemDisk Information with missing Performance level",
  521. testVar: &SlimK8sNode{
  522. InstanceType: "ecs.sn2.large",
  523. RegionID: "cn-hangzhou",
  524. PriceUnit: "Hour",
  525. MemorySizeInKiB: "16777216KiB",
  526. IsIoOptimized: true,
  527. OSType: "linux",
  528. ProviderID: "cn-hangzhou.i-test-04",
  529. InstanceTypeFamily: "sn2",
  530. SystemDisk: &SlimK8sDisk{
  531. DiskType: "system",
  532. RegionID: "cn-hangzhou",
  533. PriceUnit: "Hour",
  534. SizeInGiB: "40",
  535. DiskCategory: "cloud_efficiency",
  536. ProviderID: "d-Ali-cloud-XXX-i1",
  537. StorageClass: "",
  538. },
  539. },
  540. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize::cloud_efficiency::40",
  541. expectedError: nil,
  542. },
  543. {
  544. name: "test when node has a systemDisk Information with all information",
  545. testVar: &SlimK8sNode{
  546. InstanceType: "ecs.sn2.large",
  547. RegionID: "cn-hangzhou",
  548. PriceUnit: "Hour",
  549. MemorySizeInKiB: "16777216KiB",
  550. IsIoOptimized: true,
  551. OSType: "linux",
  552. ProviderID: "cn-hangzhou.i-test-04",
  553. InstanceTypeFamily: "sn2",
  554. SystemDisk: &SlimK8sDisk{
  555. DiskType: "data",
  556. RegionID: "cn-hangzhou",
  557. PriceUnit: "Hour",
  558. SizeInGiB: "80",
  559. DiskCategory: "cloud_ssd",
  560. PerformanceLevel: "PL2",
  561. ProviderID: "d-Ali-cloud-XXX-04",
  562. StorageClass: "",
  563. },
  564. },
  565. expectedKey: "cn-hangzhou::ecs.sn2.large::linux::optimize::cloud_ssd::80::PL2",
  566. expectedError: nil,
  567. },
  568. {
  569. name: "test random k8s struct should return unsupported error",
  570. testVar: &randomK8sStruct{
  571. name: "test struct",
  572. },
  573. expectedKey: "",
  574. expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
  575. },
  576. {
  577. name: "test for nil check",
  578. testVar: nil,
  579. expectedKey: "",
  580. expectedError: fmt.Errorf("unsupported ECS type randomK8sStruct for DescribePrice at this time"),
  581. },
  582. {
  583. name: "test when all RegionID, InstanceType, OSType & ALIBABA_OPTIMIZE_KEYWORD words are used to key",
  584. testVar: &SlimK8sDisk{
  585. DiskType: "data",
  586. RegionID: "cn-hangzhou",
  587. PriceUnit: "Hour",
  588. SizeInGiB: "40",
  589. DiskCategory: "cloud_efficiency",
  590. ProviderID: "d-Ali-cloud-XXX-02",
  591. StorageClass: "temp",
  592. },
  593. expectedKey: "cn-hangzhou::data::cloud_efficiency::40",
  594. expectedError: nil,
  595. },
  596. {
  597. name: "test missing InstanceType to create key",
  598. testVar: &SlimK8sDisk{
  599. DiskType: "data",
  600. RegionID: "cn-hangzhou",
  601. PriceUnit: "Hour",
  602. SizeInGiB: "80",
  603. DiskCategory: "cloud_ssd",
  604. PerformanceLevel: "PL2",
  605. ProviderID: "d-Ali-cloud-XXX-04",
  606. StorageClass: "temp",
  607. },
  608. expectedKey: "cn-hangzhou::data::cloud_ssd::PL2::80",
  609. expectedError: nil,
  610. },
  611. }
  612. for _, c := range cases {
  613. t.Run(c.name, func(t *testing.T) {
  614. returnString, returnErr := determineKeyForPricing(c.testVar)
  615. if c.expectedError == nil && returnErr != nil {
  616. t.Fatalf("Case name %s: expected error was nil but received error %v", c.name, returnErr)
  617. }
  618. if returnString != c.expectedKey {
  619. t.Fatalf("Case name %s: determineKeyForPricing received %s but expected %s", c.name, returnString, c.expectedKey)
  620. }
  621. })
  622. }
  623. }
  624. func TestGenerateSlimK8sNodeFromV1Node(t *testing.T) {
  625. testv1Node := &v1.Node{}
  626. testv1Node.Labels = make(map[string]string)
  627. testv1Node.Labels["topology.kubernetes.io/region"] = "us-east-1"
  628. testv1Node.Labels["beta.kubernetes.io/os"] = "linux"
  629. testv1Node.Labels["node.kubernetes.io/instance-type"] = "ecs.sn2ne.2xlarge"
  630. testv1Node.Status.Capacity = v1.ResourceList{
  631. v1.ResourceMemory: *resource.NewQuantity(16, resource.BinarySI),
  632. }
  633. cases := []struct {
  634. name string
  635. testNode *v1.Node
  636. expectedSlimNode *SlimK8sNode
  637. }{
  638. {
  639. name: "test a generic *v1.Node to *SlimK8sNode Conversion",
  640. testNode: testv1Node,
  641. expectedSlimNode: &SlimK8sNode{
  642. InstanceType: "ecs.sn2ne.2xlarge",
  643. RegionID: "us-east-1",
  644. PriceUnit: ALIBABA_HOUR_PRICE_UNIT,
  645. MemorySizeInKiB: "16",
  646. IsIoOptimized: true,
  647. OSType: "linux",
  648. InstanceTypeFamily: "sn2ne",
  649. },
  650. },
  651. }
  652. for _, c := range cases {
  653. t.Run(c.name, func(t *testing.T) {
  654. returnSlimK8sNode := generateSlimK8sNodeFromV1Node(c.testNode)
  655. if returnSlimK8sNode.InstanceType != c.expectedSlimNode.InstanceType {
  656. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceType: %s , received InstanceType: %s", c.expectedSlimNode.InstanceType, returnSlimK8sNode.InstanceType)
  657. }
  658. if returnSlimK8sNode.RegionID != c.expectedSlimNode.RegionID {
  659. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected RegionID: %s , received RegionID: %s", c.expectedSlimNode.RegionID, returnSlimK8sNode.RegionID)
  660. }
  661. if returnSlimK8sNode.PriceUnit != c.expectedSlimNode.PriceUnit {
  662. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected PriceUnit: %s , received PriceUnit: %s", c.expectedSlimNode.PriceUnit, returnSlimK8sNode.PriceUnit)
  663. }
  664. if returnSlimK8sNode.MemorySizeInKiB != c.expectedSlimNode.MemorySizeInKiB {
  665. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected MemorySizeInKiB: %s , received MemorySizeInKiB: %s", c.expectedSlimNode.MemorySizeInKiB, returnSlimK8sNode.MemorySizeInKiB)
  666. }
  667. if returnSlimK8sNode.OSType != c.expectedSlimNode.OSType {
  668. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected OSType: %s , received OSType: %s", c.expectedSlimNode.OSType, returnSlimK8sNode.OSType)
  669. }
  670. if returnSlimK8sNode.InstanceTypeFamily != c.expectedSlimNode.InstanceTypeFamily {
  671. t.Fatalf("unexpected conversion in function generateSlimK8sNodeFromV1Node expected InstanceTypeFamily: %s , received InstanceTypeFamily: %s", c.expectedSlimNode.InstanceTypeFamily, returnSlimK8sNode.InstanceTypeFamily)
  672. }
  673. })
  674. }
  675. }
  676. func TestGenerateSlimK8sDiskFromV1PV(t *testing.T) {
  677. testv1PV := &v1.PersistentVolume{}
  678. testv1PV.Spec.Capacity = v1.ResourceList{
  679. v1.ResourceStorage: *resource.NewQuantity(16*1024*1024*1024, resource.BinarySI),
  680. }
  681. testv1PV.Spec.CSI = &v1.CSIPersistentVolumeSource{}
  682. testv1PV.Spec.CSI.VolumeHandle = "testPV"
  683. testv1PV.Spec.CSI.VolumeAttributes = map[string]string{
  684. "performanceLevel": "PL2",
  685. "type": "cloud_essd",
  686. }
  687. testv1PV.Spec.CSI.VolumeHandle = "testPV"
  688. testv1PV.Spec.StorageClassName = "testStorageClass"
  689. cases := []struct {
  690. name string
  691. testPV *v1.PersistentVolume
  692. expectedSlimDisk *SlimK8sDisk
  693. inpRegionID string
  694. }{
  695. {
  696. name: "test a generic *v1.Node to *SlimK8sNode Conversion",
  697. testPV: testv1PV,
  698. expectedSlimDisk: &SlimK8sDisk{
  699. DiskType: ALIBABA_DATA_DISK_CATEGORY,
  700. RegionID: "us-east-1",
  701. PriceUnit: ALIBABA_HOUR_PRICE_UNIT,
  702. SizeInGiB: "16",
  703. DiskCategory: "cloud_essd",
  704. PerformanceLevel: "PL2",
  705. ProviderID: "testPV",
  706. StorageClass: "testStorageClass",
  707. },
  708. inpRegionID: "us-east-1",
  709. },
  710. }
  711. for _, c := range cases {
  712. t.Run(c.name, func(t *testing.T) {
  713. returnSlimK8sDisk := generateSlimK8sDiskFromV1PV(c.testPV, c.inpRegionID)
  714. if returnSlimK8sDisk.DiskType != c.expectedSlimDisk.DiskType {
  715. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected DiskType: %s , received DiskType: %s", c.expectedSlimDisk.DiskType, returnSlimK8sDisk.DiskType)
  716. }
  717. if returnSlimK8sDisk.RegionID != c.expectedSlimDisk.RegionID {
  718. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected RegionID: %s , received RegionID Type: %s", c.expectedSlimDisk.RegionID, returnSlimK8sDisk.RegionID)
  719. }
  720. if returnSlimK8sDisk.PriceUnit != c.expectedSlimDisk.PriceUnit {
  721. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected PriceUnit: %s , received PriceUnit Type: %s", c.expectedSlimDisk.PriceUnit, returnSlimK8sDisk.PriceUnit)
  722. }
  723. if returnSlimK8sDisk.SizeInGiB != c.expectedSlimDisk.SizeInGiB {
  724. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected SizeInGiB: %s , received SizeInGiB Type: %s", c.expectedSlimDisk.SizeInGiB, returnSlimK8sDisk.SizeInGiB)
  725. }
  726. if returnSlimK8sDisk.DiskCategory != c.expectedSlimDisk.DiskCategory {
  727. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected DiskCategory: %s , received DiskCategory Type: %s", c.expectedSlimDisk.DiskCategory, returnSlimK8sDisk.DiskCategory)
  728. }
  729. if returnSlimK8sDisk.PerformanceLevel != c.expectedSlimDisk.PerformanceLevel {
  730. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected PerformanceLevel: %s , received PerformanceLevel Type: %s", c.expectedSlimDisk.PerformanceLevel, returnSlimK8sDisk.PerformanceLevel)
  731. }
  732. if returnSlimK8sDisk.ProviderID != c.expectedSlimDisk.ProviderID {
  733. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected ProviderID: %s , received ProviderID Type: %s", c.expectedSlimDisk.ProviderID, returnSlimK8sDisk.ProviderID)
  734. }
  735. if returnSlimK8sDisk.StorageClass != c.expectedSlimDisk.StorageClass {
  736. t.Fatalf("unexpected conversion in function generateSlimK8sDiskFromV1PV expected StorageClass: %s , received StorageClass Type: %s", c.expectedSlimDisk.StorageClass, returnSlimK8sDisk.StorageClass)
  737. }
  738. })
  739. }
  740. }
  741. func TestGetNumericalValueFromResourceQuantity(t *testing.T) {
  742. cases := []struct {
  743. name string
  744. inputResourceQuanity string
  745. expectedValue string
  746. }{
  747. {
  748. name: "positive scenario: when inputResourceQuantity is 10Gi",
  749. inputResourceQuanity: "10Gi",
  750. expectedValue: "10",
  751. },
  752. {
  753. name: "negative scenario: when inputResourceQuantity is Gi",
  754. inputResourceQuanity: "Gi",
  755. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  756. },
  757. {
  758. name: "negative scenario: when inputResourceQuantity is 10",
  759. inputResourceQuanity: "10",
  760. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  761. },
  762. {
  763. name: "negative scenario: when inputResourceQuantity is empty string",
  764. inputResourceQuanity: "",
  765. expectedValue: ALIBABA_DEFAULT_DATADISK_SIZE,
  766. },
  767. }
  768. for _, c := range cases {
  769. t.Run(c.name, func(t *testing.T) {
  770. returnValue := getNumericalValueFromResourceQuantity(c.inputResourceQuanity)
  771. if c.expectedValue != returnValue {
  772. t.Fatalf("Case name %s: getNumericalValueFromResourceQuantity received %s but expected %s", c.name, returnValue, c.expectedValue)
  773. }
  774. })
  775. }
  776. }
  777. func TestDeterminePVRegion(t *testing.T) {
  778. genericNodeAffinityTestStruct := v1.NodeSelectorTerm{
  779. MatchExpressions: []v1.NodeSelectorRequirement{
  780. {
  781. Key: "topology.diskplugin.csi.alibabacloud.com/zone",
  782. Operator: v1.NodeSelectorOpIn,
  783. Values: []string{"us-east-1a"},
  784. },
  785. },
  786. MatchFields: []v1.NodeSelectorRequirement{},
  787. }
  788. // testPV1 contains the Label with region information as well as node affinity in spec
  789. testPV1 := &v1.PersistentVolume{}
  790. testPV1.Name = "testPV1"
  791. testPV1.Labels = make(map[string]string)
  792. testPV1.Labels[ALIBABA_DISK_TOPOLOGY_REGION_LABEL] = "us-east-1"
  793. testPV1.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
  794. Required: &v1.NodeSelector{
  795. NodeSelectorTerms: []v1.NodeSelectorTerm{genericNodeAffinityTestStruct},
  796. },
  797. }
  798. // testPV2 contains the only zone label
  799. testPV2 := &v1.PersistentVolume{}
  800. testPV2.Name = "testPV2"
  801. testPV2.Labels = make(map[string]string)
  802. testPV2.Labels[ALIBABA_DISK_TOPOLOGY_ZONE_LABEL] = "us-east-1a"
  803. // testPV3 contains only node affinity in spec
  804. testPV3 := &v1.PersistentVolume{}
  805. testPV3.Name = "testPV3"
  806. testPV3.Spec.NodeAffinity = &v1.VolumeNodeAffinity{
  807. Required: &v1.NodeSelector{
  808. NodeSelectorTerms: []v1.NodeSelectorTerm{genericNodeAffinityTestStruct},
  809. },
  810. }
  811. // testPV4 contains no label/annotation or any node affinity
  812. testPV4 := &v1.PersistentVolume{}
  813. testPV4.Name = "testPV4"
  814. cases := []struct {
  815. name string
  816. inputPV *v1.PersistentVolume
  817. expectedRegion string
  818. }{
  819. {
  820. name: "When Region label topology.diskplugin.csi.alibabacloud.com/region is present along with node affinity details",
  821. inputPV: testPV1,
  822. expectedRegion: "us-east-1",
  823. },
  824. {
  825. name: "When zone label topology.diskplugin.csi.alibabacloud.com/zone is present function has to determine region",
  826. inputPV: testPV2,
  827. expectedRegion: "us-east-1",
  828. },
  829. {
  830. name: "When only node affinity detail is present function has to determine the region",
  831. inputPV: testPV3,
  832. expectedRegion: "us-east-1",
  833. },
  834. {
  835. name: "When no region/zone information is present function returns empty to default to cluster region",
  836. inputPV: testPV4,
  837. expectedRegion: "",
  838. },
  839. }
  840. for _, c := range cases {
  841. t.Run(c.name, func(t *testing.T) {
  842. returnRegion := determinePVRegion(c.inputPV)
  843. if c.expectedRegion != returnRegion {
  844. t.Fatalf("Case name %s: determinePVRegion received region :%s but expected region: %s", c.name, returnRegion, c.expectedRegion)
  845. }
  846. })
  847. }
  848. }
  849. func TestGetInstanceFamilyGenerationFromType(t *testing.T) {
  850. cases := []struct {
  851. name string
  852. instanceType string
  853. expectedInstanceFamilyGeneration int
  854. }{
  855. {
  856. name: "test if ecs.[instance-family].[different-type] work",
  857. instanceType: "ecs.sn2ne.2xlarge",
  858. expectedInstanceFamilyGeneration: 2,
  859. },
  860. {
  861. name: "test if ecs.[instance-family].[different-type] work",
  862. instanceType: "ecs.g7.large",
  863. expectedInstanceFamilyGeneration: 7,
  864. },
  865. {
  866. name: "test if random word gives you ALIBABA_UNKNOWN_INSTANCE_FAMILY_TYPE value ",
  867. instanceType: "random.value",
  868. expectedInstanceFamilyGeneration: -1,
  869. },
  870. }
  871. for _, c := range cases {
  872. t.Run(c.name, func(t *testing.T) {
  873. returnValue := getInstanceFamilyGenerationFromType(c.instanceType)
  874. if returnValue != c.expectedInstanceFamilyGeneration {
  875. t.Fatalf("Case name %s: expected instance family generation of type %d but got %d", c.name, c.expectedInstanceFamilyGeneration, returnValue)
  876. }
  877. })
  878. }
  879. }
  880. func TestCreateDescribeNodePriceACSRequest(t *testing.T) {
  881. cases := []struct {
  882. name string
  883. testStruct interface{}
  884. expectedError error
  885. expectedDiskCategory string
  886. }{
  887. {
  888. // Test case for instance type ecs.g6.large
  889. name: "test request parma when instance type is ecs.g6.large",
  890. testStruct: &SlimK8sNode{
  891. InstanceType: "ecs.g6.large",
  892. RegionID: "cn-hangzhou",
  893. PriceUnit: "Hour",
  894. MemorySizeInKiB: "16KiB",
  895. IsIoOptimized: true,
  896. OSType: "Linux",
  897. ProviderID: "Ali-XXX-node-01",
  898. InstanceTypeFamily: "g6",
  899. },
  900. expectedError: nil,
  901. expectedDiskCategory: "",
  902. },
  903. {
  904. // Test case for instance type ecs.g7.large
  905. name: "test request parma when instance type is ecs.g7.large",
  906. testStruct: &SlimK8sNode{
  907. InstanceType: "ecs.g7.large",
  908. RegionID: "cn-hangzhou",
  909. PriceUnit: "Hour",
  910. MemorySizeInKiB: "16KiB",
  911. IsIoOptimized: true,
  912. OSType: "Linux",
  913. ProviderID: "Ali-XXX-node-02",
  914. InstanceTypeFamily: "g7",
  915. },
  916. expectedError: nil,
  917. expectedDiskCategory: ALIBABA_DISK_CLOUD_ESSD_CATEGORY,
  918. },
  919. {
  920. // Test case for instance type ecs.g7.large, this instance type is in 'alibabaDefaultToCloudEssd'
  921. name: "test request parma when instance type is ecs.g6e.large",
  922. testStruct: &SlimK8sNode{
  923. InstanceType: "ecs.g6e.large",
  924. RegionID: "cn-hangzhou",
  925. PriceUnit: "Hour",
  926. MemorySizeInKiB: "16KiB",
  927. IsIoOptimized: true,
  928. OSType: "Linux",
  929. ProviderID: "Ali-XXX-node-03",
  930. InstanceTypeFamily: "g6e",
  931. },
  932. expectedError: nil,
  933. expectedDiskCategory: ALIBABA_DISK_CLOUD_ESSD_CATEGORY,
  934. },
  935. }
  936. for _, c := range cases {
  937. t.Run(c.name, func(t *testing.T) {
  938. req, err := createDescribePriceACSRequest(c.testStruct)
  939. t.Logf("Request Params SystemDisk.Category: %v", req.QueryParams["SystemDisk.Category"])
  940. if err != nil && c.expectedError != nil {
  941. t.Fatalf("Case name %s: Error converting to Alibaba cloud request", c.name)
  942. }
  943. if c.expectedDiskCategory != req.QueryParams["SystemDisk.Category"] {
  944. t.Fatalf("Case name %s: Disk Category is not set correctly", c.name)
  945. }
  946. })
  947. }
  948. }