convert_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. /*
  2. Copyright 2020 Docker Compose CLI authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package convert
  14. import (
  15. "context"
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "path"
  20. "testing"
  21. "github.com/stretchr/testify/mock"
  22. "github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
  23. "github.com/Azure/go-autorest/autorest/to"
  24. "github.com/compose-spec/compose-go/types"
  25. "gotest.tools/v3/assert"
  26. is "gotest.tools/v3/assert/cmp"
  27. "github.com/docker/compose-cli/api/compose"
  28. "github.com/docker/compose-cli/api/containers"
  29. "github.com/docker/compose-cli/context/store"
  30. )
  31. var (
  32. convertCtx = store.AciContext{
  33. SubscriptionID: "subID",
  34. ResourceGroup: "rg",
  35. Location: "eu",
  36. }
  37. mockStorageHelper = &mockStorageLogin{}
  38. )
  39. func TestProjectName(t *testing.T) {
  40. project := types.Project{
  41. Name: "TEST",
  42. }
  43. containerGroup, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  44. assert.NilError(t, err)
  45. assert.Equal(t, *containerGroup.Name, "test")
  46. }
  47. func TestContainerGroupToContainer(t *testing.T) {
  48. myContainerGroup := containerinstance.ContainerGroup{
  49. ContainerGroupProperties: &containerinstance.ContainerGroupProperties{
  50. IPAddress: &containerinstance.IPAddress{
  51. Ports: &[]containerinstance.Port{{
  52. Port: to.Int32Ptr(80),
  53. }},
  54. IP: to.StringPtr("42.42.42.42"),
  55. DNSNameLabel: to.StringPtr("myapp"),
  56. },
  57. OsType: "Linux",
  58. },
  59. }
  60. myContainer := containerinstance.Container{
  61. Name: to.StringPtr("myContainerID"),
  62. ContainerProperties: &containerinstance.ContainerProperties{
  63. Image: to.StringPtr("sha256:666"),
  64. Command: to.StringSlicePtr([]string{"mycommand"}),
  65. Ports: &[]containerinstance.ContainerPort{{
  66. Port: to.Int32Ptr(80),
  67. }},
  68. EnvironmentVariables: nil,
  69. InstanceView: &containerinstance.ContainerPropertiesInstanceView{
  70. RestartCount: nil,
  71. CurrentState: &containerinstance.ContainerState{
  72. State: to.StringPtr("Running"),
  73. },
  74. },
  75. Resources: &containerinstance.ResourceRequirements{
  76. Limits: &containerinstance.ResourceLimits{
  77. CPU: to.Float64Ptr(3),
  78. MemoryInGB: to.Float64Ptr(0.2),
  79. },
  80. Requests: &containerinstance.ResourceRequests{
  81. CPU: to.Float64Ptr(2),
  82. MemoryInGB: to.Float64Ptr(0.1),
  83. },
  84. },
  85. },
  86. }
  87. var expectedContainer = containers.Container{
  88. ID: "myContainerID",
  89. Status: "Running",
  90. Image: "sha256:666",
  91. Command: "mycommand",
  92. Platform: "Linux",
  93. Ports: []containers.Port{{
  94. HostPort: uint32(80),
  95. ContainerPort: uint32(80),
  96. Protocol: "tcp",
  97. HostIP: "42.42.42.42",
  98. }},
  99. Config: &containers.RuntimeConfig{
  100. FQDN: "myapp.eastus.azurecontainer.io",
  101. },
  102. HostConfig: &containers.HostConfig{
  103. CPULimit: 3,
  104. CPUReservation: 2,
  105. MemoryLimit: gbToBytes(0.2),
  106. MemoryReservation: gbToBytes(0.1),
  107. RestartPolicy: "any",
  108. },
  109. }
  110. container := ContainerGroupToContainer("myContainerID", myContainerGroup, myContainer, "eastus")
  111. assert.DeepEqual(t, container, expectedContainer)
  112. }
  113. func TestContainerGroupToServiceStatus(t *testing.T) {
  114. myContainerGroup := containerinstance.ContainerGroup{
  115. ContainerGroupProperties: &containerinstance.ContainerGroupProperties{
  116. IPAddress: &containerinstance.IPAddress{
  117. Ports: &[]containerinstance.Port{{
  118. Port: to.Int32Ptr(80),
  119. }},
  120. IP: to.StringPtr("42.42.42.42"),
  121. },
  122. },
  123. }
  124. myContainer := containerinstance.Container{
  125. Name: to.StringPtr("myContainerID"),
  126. ContainerProperties: &containerinstance.ContainerProperties{
  127. Ports: &[]containerinstance.ContainerPort{{
  128. Port: to.Int32Ptr(80),
  129. }},
  130. InstanceView: &containerinstance.ContainerPropertiesInstanceView{
  131. RestartCount: nil,
  132. CurrentState: &containerinstance.ContainerState{
  133. State: to.StringPtr("Running"),
  134. },
  135. },
  136. },
  137. }
  138. var expectedService = compose.ServiceStatus{
  139. ID: "myContainerID",
  140. Name: "myContainerID",
  141. Ports: []string{"42.42.42.42:80->80/tcp"},
  142. Replicas: 1,
  143. Desired: 1,
  144. }
  145. container := ContainerGroupToServiceStatus("myContainerID", myContainerGroup, myContainer, "eastus")
  146. assert.DeepEqual(t, container, expectedService)
  147. }
  148. func TestComposeContainerGroupToContainerWithDnsSideCarSide(t *testing.T) {
  149. project := types.Project{
  150. Services: []types.ServiceConfig{
  151. {
  152. Name: "service1",
  153. Image: "image1",
  154. },
  155. {
  156. Name: "service2",
  157. Image: "image2",
  158. },
  159. },
  160. }
  161. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  162. assert.NilError(t, err)
  163. assert.Assert(t, is.Len(*group.Containers, 3))
  164. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  165. assert.Equal(t, *(*group.Containers)[1].Name, "service2")
  166. assert.Equal(t, *(*group.Containers)[2].Name, ComposeDNSSidecarName)
  167. assert.DeepEqual(t, *(*group.Containers)[2].Command, []string{"sh", "-c", "echo 127.0.0.1 service1 >> /etc/hosts;echo 127.0.0.1 service2 >> /etc/hosts;sleep infinity"})
  168. assert.Equal(t, *(*group.Containers)[0].Image, "image1")
  169. assert.Equal(t, *(*group.Containers)[1].Image, "image2")
  170. assert.Equal(t, *(*group.Containers)[2].Image, dnsSidecarImage)
  171. }
  172. func TestComposeSingleContainerGroupToContainerNoDnsSideCarSide(t *testing.T) {
  173. project := types.Project{
  174. Services: []types.ServiceConfig{
  175. {
  176. Name: "service1",
  177. Image: "image1",
  178. },
  179. },
  180. }
  181. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  182. assert.NilError(t, err)
  183. assert.Assert(t, is.Len(*group.Containers, 1))
  184. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  185. assert.Equal(t, *(*group.Containers)[0].Image, "image1")
  186. }
  187. func TestComposeVolumes(t *testing.T) {
  188. ctx := context.TODO()
  189. accountName := "myAccount"
  190. mockStorageHelper.On("GetAzureStorageAccountKey", ctx, accountName).Return("123456", nil)
  191. project := types.Project{
  192. Services: []types.ServiceConfig{
  193. {
  194. Name: "service1",
  195. Image: "image1",
  196. },
  197. },
  198. Volumes: types.Volumes{
  199. "vol1": types.VolumeConfig{
  200. Driver: "azure_file",
  201. DriverOpts: map[string]string{
  202. "share_name": "myFileshare",
  203. "storage_account_name": accountName,
  204. },
  205. },
  206. },
  207. }
  208. group, err := ToContainerGroup(ctx, convertCtx, project, mockStorageHelper)
  209. assert.NilError(t, err)
  210. assert.Assert(t, is.Len(*group.Containers, 1))
  211. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  212. expectedGroupVolume := containerinstance.Volume{
  213. Name: to.StringPtr("vol1"),
  214. AzureFile: &containerinstance.AzureFileVolume{
  215. ShareName: to.StringPtr("myFileshare"),
  216. StorageAccountName: &accountName,
  217. StorageAccountKey: to.StringPtr("123456"),
  218. ReadOnly: to.BoolPtr(false),
  219. },
  220. }
  221. assert.Equal(t, len(*group.Volumes), 1)
  222. assert.DeepEqual(t, (*group.Volumes)[0], expectedGroupVolume)
  223. }
  224. func TestComposeVolumesRO(t *testing.T) {
  225. ctx := context.TODO()
  226. accountName := "myAccount"
  227. mockStorageHelper.On("GetAzureStorageAccountKey", ctx, accountName).Return("123456", nil)
  228. project := types.Project{
  229. Services: []types.ServiceConfig{
  230. {
  231. Name: "service1",
  232. Image: "image1",
  233. },
  234. },
  235. Volumes: types.Volumes{
  236. "vol1": types.VolumeConfig{
  237. Driver: "azure_file",
  238. DriverOpts: map[string]string{
  239. "share_name": "myFileshare",
  240. "storage_account_name": accountName,
  241. "read_only": "true",
  242. },
  243. },
  244. },
  245. }
  246. group, err := ToContainerGroup(ctx, convertCtx, project, mockStorageHelper)
  247. assert.NilError(t, err)
  248. assert.Assert(t, is.Len(*group.Containers, 1))
  249. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  250. expectedGroupVolume := containerinstance.Volume{
  251. Name: to.StringPtr("vol1"),
  252. AzureFile: &containerinstance.AzureFileVolume{
  253. ShareName: to.StringPtr("myFileshare"),
  254. StorageAccountName: &accountName,
  255. StorageAccountKey: to.StringPtr("123456"),
  256. ReadOnly: to.BoolPtr(true),
  257. },
  258. }
  259. assert.Equal(t, len(*group.Volumes), 1)
  260. assert.DeepEqual(t, (*group.Volumes)[0], expectedGroupVolume)
  261. }
  262. type mockStorageLogin struct {
  263. mock.Mock
  264. }
  265. func (s *mockStorageLogin) GetAzureStorageAccountKey(ctx context.Context, accountName string) (string, error) {
  266. args := s.Called(ctx, accountName)
  267. return args.String(0), args.Error(1)
  268. }
  269. func TestComposeSingleContainerRestartPolicy(t *testing.T) {
  270. project := types.Project{
  271. Services: []types.ServiceConfig{
  272. {
  273. Name: "service1",
  274. Image: "image1",
  275. Deploy: &types.DeployConfig{
  276. RestartPolicy: &types.RestartPolicy{
  277. Condition: "on-failure",
  278. },
  279. },
  280. },
  281. },
  282. }
  283. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  284. assert.NilError(t, err)
  285. assert.Assert(t, is.Len(*group.Containers, 1))
  286. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  287. assert.Equal(t, group.RestartPolicy, containerinstance.OnFailure)
  288. }
  289. func TestComposeMultiContainerRestartPolicy(t *testing.T) {
  290. project := types.Project{
  291. Services: []types.ServiceConfig{
  292. {
  293. Name: "service1",
  294. Image: "image1",
  295. Deploy: &types.DeployConfig{
  296. RestartPolicy: &types.RestartPolicy{
  297. Condition: "on-failure",
  298. },
  299. },
  300. },
  301. {
  302. Name: "service2",
  303. Image: "image2",
  304. Deploy: &types.DeployConfig{
  305. RestartPolicy: &types.RestartPolicy{
  306. Condition: "on-failure",
  307. },
  308. },
  309. },
  310. },
  311. }
  312. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  313. assert.NilError(t, err)
  314. assert.Assert(t, is.Len(*group.Containers, 3))
  315. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  316. assert.Equal(t, group.RestartPolicy, containerinstance.OnFailure)
  317. assert.Equal(t, *(*group.Containers)[1].Name, "service2")
  318. assert.Equal(t, group.RestartPolicy, containerinstance.OnFailure)
  319. }
  320. func TestComposeInconsistentMultiContainerRestartPolicy(t *testing.T) {
  321. project := types.Project{
  322. Services: []types.ServiceConfig{
  323. {
  324. Name: "service1",
  325. Image: "image1",
  326. Deploy: &types.DeployConfig{
  327. RestartPolicy: &types.RestartPolicy{
  328. Condition: "any",
  329. },
  330. },
  331. },
  332. {
  333. Name: "service2",
  334. Image: "image2",
  335. Deploy: &types.DeployConfig{
  336. RestartPolicy: &types.RestartPolicy{
  337. Condition: "on-failure",
  338. },
  339. },
  340. },
  341. },
  342. }
  343. _, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  344. assert.Error(t, err, "ACI integration does not support specifying different restart policies on services in the same compose application")
  345. }
  346. func TestLabelsErrorMessage(t *testing.T) {
  347. project := types.Project{
  348. Services: []types.ServiceConfig{
  349. {
  350. Name: "service1",
  351. Image: "image1",
  352. Labels: map[string]string{
  353. "label1": "value1",
  354. },
  355. },
  356. },
  357. }
  358. _, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  359. assert.Error(t, err, "ACI integration does not support labels in compose applications")
  360. }
  361. func TestComposeSingleContainerGroupToContainerDefaultRestartPolicy(t *testing.T) {
  362. project := types.Project{
  363. Services: []types.ServiceConfig{
  364. {
  365. Name: "service1",
  366. Image: "image1",
  367. },
  368. },
  369. }
  370. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  371. assert.NilError(t, err)
  372. assert.Assert(t, is.Len(*group.Containers, 1))
  373. assert.Equal(t, *(*group.Containers)[0].Name, "service1")
  374. assert.Equal(t, group.RestartPolicy, containerinstance.Always)
  375. }
  376. func TestComposeContainerGroupToContainerMultiplePorts(t *testing.T) {
  377. project := types.Project{
  378. Services: []types.ServiceConfig{
  379. {
  380. Name: "service1",
  381. Image: "image1",
  382. Ports: []types.ServicePortConfig{
  383. {
  384. Published: 80,
  385. Target: 80,
  386. },
  387. },
  388. },
  389. {
  390. Name: "service2",
  391. Image: "image2",
  392. Ports: []types.ServicePortConfig{
  393. {
  394. Published: 8080,
  395. Target: 8080,
  396. },
  397. },
  398. },
  399. },
  400. }
  401. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  402. assert.NilError(t, err)
  403. assert.Assert(t, is.Len(*group.Containers, 3))
  404. container1 := (*group.Containers)[0]
  405. assert.Equal(t, *container1.Name, "service1")
  406. assert.Equal(t, *container1.Image, "image1")
  407. assert.Equal(t, *(*container1.Ports)[0].Port, int32(80))
  408. container2 := (*group.Containers)[1]
  409. assert.Equal(t, *container2.Name, "service2")
  410. assert.Equal(t, *container2.Image, "image2")
  411. assert.Equal(t, *(*container2.Ports)[0].Port, int32(8080))
  412. groupPorts := *group.IPAddress.Ports
  413. assert.Assert(t, is.Len(groupPorts, 2))
  414. assert.Equal(t, *groupPorts[0].Port, int32(80))
  415. assert.Equal(t, *groupPorts[1].Port, int32(8080))
  416. assert.Assert(t, group.IPAddress.DNSNameLabel == nil)
  417. }
  418. func TestComposeContainerGroupToContainerWithDomainName(t *testing.T) {
  419. project := types.Project{
  420. Services: []types.ServiceConfig{
  421. {
  422. Name: "service1",
  423. Image: "image1",
  424. Ports: []types.ServicePortConfig{
  425. {
  426. Published: 80,
  427. Target: 80,
  428. },
  429. },
  430. DomainName: "myApp",
  431. },
  432. {
  433. Name: "service2",
  434. Image: "image2",
  435. Ports: []types.ServicePortConfig{
  436. {
  437. Published: 8080,
  438. Target: 8080,
  439. },
  440. },
  441. },
  442. },
  443. }
  444. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  445. assert.NilError(t, err)
  446. assert.Assert(t, is.Len(*group.Containers, 3))
  447. groupPorts := *group.IPAddress.Ports
  448. assert.Assert(t, is.Len(groupPorts, 2))
  449. assert.Equal(t, *groupPorts[0].Port, int32(80))
  450. assert.Equal(t, *groupPorts[1].Port, int32(8080))
  451. assert.Equal(t, *group.IPAddress.DNSNameLabel, "myApp")
  452. }
  453. func TestComposeContainerGroupToContainerErrorWhenSeveralDomainNames(t *testing.T) {
  454. project := types.Project{
  455. Services: []types.ServiceConfig{
  456. {
  457. Name: "service1",
  458. Image: "image1",
  459. DomainName: "myApp",
  460. },
  461. {
  462. Name: "service2",
  463. Image: "image2",
  464. DomainName: "myApp2",
  465. },
  466. },
  467. }
  468. _, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  469. assert.Error(t, err, "ACI integration does not support specifying different domain names on services in the same compose application")
  470. }
  471. // ACI fails if group definition IPAddress has no ports
  472. func TestComposeContainerGroupToContainerIgnoreDomainNameWithoutPorts(t *testing.T) {
  473. project := types.Project{
  474. Services: []types.ServiceConfig{
  475. {
  476. Name: "service1",
  477. Image: "image1",
  478. DomainName: "myApp",
  479. },
  480. {
  481. Name: "service2",
  482. Image: "image2",
  483. DomainName: "myApp",
  484. },
  485. },
  486. }
  487. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  488. assert.NilError(t, err)
  489. assert.Assert(t, is.Len(*group.Containers, 3))
  490. assert.Assert(t, group.IPAddress == nil)
  491. }
  492. var _0_1Gb = gbToBytes(0.1)
  493. func TestComposeContainerGroupToContainerResourceRequests(t *testing.T) {
  494. project := types.Project{
  495. Services: []types.ServiceConfig{
  496. {
  497. Name: "service1",
  498. Image: "image1",
  499. Deploy: &types.DeployConfig{
  500. Resources: types.Resources{
  501. Reservations: &types.Resource{
  502. NanoCPUs: "0.1",
  503. MemoryBytes: types.UnitBytes(_0_1Gb),
  504. },
  505. },
  506. },
  507. },
  508. },
  509. }
  510. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  511. assert.NilError(t, err)
  512. request := *((*group.Containers)[0]).Resources.Requests
  513. assert.Equal(t, *request.CPU, float64(0.1))
  514. assert.Equal(t, *request.MemoryInGB, float64(0.1))
  515. limits := *((*group.Containers)[0]).Resources.Limits
  516. assert.Equal(t, *limits.CPU, float64(0.1))
  517. assert.Equal(t, *limits.MemoryInGB, float64(0.1))
  518. }
  519. func TestComposeContainerGroupToContainerResourceRequestsAndLimits(t *testing.T) {
  520. project := types.Project{
  521. Services: []types.ServiceConfig{
  522. {
  523. Name: "service1",
  524. Image: "image1",
  525. Deploy: &types.DeployConfig{
  526. Resources: types.Resources{
  527. Reservations: &types.Resource{
  528. NanoCPUs: "0.1",
  529. MemoryBytes: types.UnitBytes(_0_1Gb),
  530. },
  531. Limits: &types.Resource{
  532. NanoCPUs: "0.3",
  533. MemoryBytes: types.UnitBytes(2 * _0_1Gb),
  534. },
  535. },
  536. },
  537. },
  538. },
  539. }
  540. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  541. assert.NilError(t, err)
  542. request := *((*group.Containers)[0]).Resources.Requests
  543. assert.Equal(t, *request.CPU, float64(0.1))
  544. assert.Equal(t, *request.MemoryInGB, float64(0.1))
  545. limits := *((*group.Containers)[0]).Resources.Limits
  546. assert.Equal(t, *limits.CPU, float64(0.3))
  547. assert.Equal(t, *limits.MemoryInGB, float64(0.2))
  548. }
  549. func TestComposeContainerGroupToContainerResourceLimitsOnly(t *testing.T) {
  550. project := types.Project{
  551. Services: []types.ServiceConfig{
  552. {
  553. Name: "service1",
  554. Image: "image1",
  555. Deploy: &types.DeployConfig{
  556. Resources: types.Resources{
  557. Limits: &types.Resource{
  558. NanoCPUs: "0.3",
  559. MemoryBytes: types.UnitBytes(2 * _0_1Gb),
  560. },
  561. },
  562. },
  563. },
  564. },
  565. }
  566. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  567. assert.NilError(t, err)
  568. request := *((*group.Containers)[0]).Resources.Requests
  569. assert.Equal(t, *request.CPU, float64(0.3))
  570. assert.Equal(t, *request.MemoryInGB, float64(0.2))
  571. limits := *((*group.Containers)[0]).Resources.Limits
  572. assert.Equal(t, *limits.CPU, float64(0.3))
  573. assert.Equal(t, *limits.MemoryInGB, float64(0.2))
  574. }
  575. func TestComposeContainerGroupToContainerResourceRequestsDefaults(t *testing.T) {
  576. project := types.Project{
  577. Services: []types.ServiceConfig{
  578. {
  579. Name: "service1",
  580. Image: "image1",
  581. Deploy: &types.DeployConfig{
  582. Resources: types.Resources{
  583. Reservations: &types.Resource{
  584. NanoCPUs: "",
  585. MemoryBytes: 0,
  586. },
  587. },
  588. },
  589. },
  590. },
  591. }
  592. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  593. assert.NilError(t, err)
  594. request := *((*group.Containers)[0]).Resources.Requests
  595. assert.Equal(t, *request.CPU, float64(1))
  596. assert.Equal(t, *request.MemoryInGB, float64(1))
  597. }
  598. func TestComposeContainerGroupToContainerenvVar(t *testing.T) {
  599. err := os.Setenv("key2", "value2")
  600. assert.NilError(t, err)
  601. project := types.Project{
  602. Services: []types.ServiceConfig{
  603. {
  604. Name: "service1",
  605. Image: "image1",
  606. Environment: types.MappingWithEquals{
  607. "key1": to.StringPtr("value1"),
  608. "key2": nil,
  609. },
  610. },
  611. },
  612. }
  613. group, err := ToContainerGroup(context.TODO(), convertCtx, project, mockStorageHelper)
  614. assert.NilError(t, err)
  615. envVars := *((*group.Containers)[0]).EnvironmentVariables
  616. assert.Assert(t, is.Len(envVars, 2))
  617. assert.Assert(t, is.Contains(envVars, containerinstance.EnvironmentVariable{Name: to.StringPtr("key1"), Value: to.StringPtr("value1")}))
  618. assert.Assert(t, is.Contains(envVars, containerinstance.EnvironmentVariable{Name: to.StringPtr("key2"), Value: to.StringPtr("value2")}))
  619. }
  620. func TestConvertToAciRestartPolicyCondition(t *testing.T) {
  621. assert.Equal(t, toAciRestartPolicy("none"), containerinstance.Never)
  622. assert.Equal(t, toAciRestartPolicy("always"), containerinstance.Always)
  623. assert.Equal(t, toAciRestartPolicy("on-failure"), containerinstance.OnFailure)
  624. assert.Equal(t, toAciRestartPolicy("on-failure:5"), containerinstance.Always)
  625. }
  626. func TestConvertToDockerRestartPolicyCondition(t *testing.T) {
  627. assert.Equal(t, toContainerRestartPolicy(containerinstance.Never), "none")
  628. assert.Equal(t, toContainerRestartPolicy(containerinstance.Always), "any")
  629. assert.Equal(t, toContainerRestartPolicy(containerinstance.OnFailure), "on-failure")
  630. assert.Equal(t, toContainerRestartPolicy(""), "any")
  631. }
  632. func TestConvertContainerGroupStatus(t *testing.T) {
  633. assert.Equal(t, "Running", GetStatus(container(to.StringPtr("Running")), group(to.StringPtr("Started"))))
  634. assert.Equal(t, "Terminated", GetStatus(container(to.StringPtr("Terminated")), group(to.StringPtr("Stopped"))))
  635. assert.Equal(t, "Node Stopped", GetStatus(container(nil), group(to.StringPtr("Stopped"))))
  636. assert.Equal(t, "Node Started", GetStatus(container(nil), group(to.StringPtr("Started"))))
  637. assert.Equal(t, "Running", GetStatus(container(to.StringPtr("Running")), group(nil)))
  638. assert.Equal(t, "Unknown", GetStatus(container(nil), group(nil)))
  639. }
  640. func TestConvertSecrets(t *testing.T) {
  641. serviceName := "testservice"
  642. secretName := "testsecret"
  643. absBasePath := "/home/user"
  644. tmpFile, err := ioutil.TempFile(os.TempDir(), "TestConvertProjectSecrets-")
  645. assert.NilError(t, err)
  646. _, err = tmpFile.Write([]byte("test content"))
  647. assert.NilError(t, err)
  648. t.Cleanup(func() {
  649. _ = os.Remove(tmpFile.Name())
  650. })
  651. t.Run("mix default and absolute", func(t *testing.T) {
  652. pSquashedDefaultAndAbs := projectAciHelper{
  653. Services: []types.ServiceConfig{
  654. {
  655. Name: serviceName,
  656. Secrets: []types.ServiceSecretConfig{
  657. {
  658. Source: secretName,
  659. Target: "some_target1",
  660. },
  661. {
  662. Source: secretName,
  663. },
  664. {
  665. Source: secretName,
  666. Target: path.Join(defaultSecretsPath, "some_target2"),
  667. },
  668. {
  669. Source: secretName,
  670. Target: path.Join(absBasePath, "some_target3"),
  671. },
  672. {
  673. Source: secretName,
  674. Target: path.Join(absBasePath, "some_target4"),
  675. },
  676. },
  677. },
  678. },
  679. Secrets: map[string]types.SecretConfig{
  680. secretName: {
  681. File: tmpFile.Name(),
  682. },
  683. },
  684. }
  685. volumes, err := pSquashedDefaultAndAbs.getAciSecretVolumes()
  686. assert.NilError(t, err)
  687. assert.Equal(t, len(volumes), 2)
  688. defaultVolumeName := getServiceSecretKey(serviceName, defaultSecretsPath)
  689. homeVolumeName := getServiceSecretKey(serviceName, absBasePath)
  690. // random order since this was created from a map...
  691. for _, vol := range volumes {
  692. switch *vol.Name {
  693. case defaultVolumeName:
  694. assert.Equal(t, len(vol.Secret), 3)
  695. case homeVolumeName:
  696. assert.Equal(t, len(vol.Secret), 2)
  697. default:
  698. assert.Assert(t, false, "unexpected volume name: "+*vol.Name)
  699. }
  700. }
  701. s := serviceConfigAciHelper(pSquashedDefaultAndAbs.Services[0])
  702. vms, err := s.getAciSecretsVolumeMounts()
  703. assert.NilError(t, err)
  704. assert.Equal(t, len(vms), 2)
  705. assert.Equal(t, *vms[0].Name, defaultVolumeName)
  706. assert.Equal(t, *vms[0].MountPath, defaultSecretsPath)
  707. assert.Equal(t, *vms[1].Name, homeVolumeName)
  708. assert.Equal(t, *vms[1].MountPath, absBasePath)
  709. })
  710. t.Run("convert invalid target", func(t *testing.T) {
  711. targetName := "some/invalid/relative/path/target"
  712. pInvalidRelativePathTarget := projectAciHelper{
  713. Services: []types.ServiceConfig{
  714. {
  715. Name: serviceName,
  716. Secrets: []types.ServiceSecretConfig{
  717. {
  718. Source: secretName,
  719. Target: targetName,
  720. },
  721. },
  722. },
  723. },
  724. Secrets: map[string]types.SecretConfig{
  725. secretName: {
  726. File: tmpFile.Name(),
  727. },
  728. },
  729. }
  730. _, err := pInvalidRelativePathTarget.getAciSecretVolumes()
  731. assert.Equal(t, err.Error(),
  732. fmt.Sprintf(`in service %q, secret with source %q cannot have a relative path as target. Only absolute paths are allowed. Found %q`,
  733. serviceName, secretName, targetName))
  734. })
  735. t.Run("convert colliding default targets", func(t *testing.T) {
  736. targetName1 := path.Join(defaultSecretsPath, "target1")
  737. targetName2 := path.Join(defaultSecretsPath, "sub/folder/target2")
  738. service := serviceConfigAciHelper{
  739. Name: serviceName,
  740. Secrets: []types.ServiceSecretConfig{
  741. {
  742. Source: secretName,
  743. Target: targetName1,
  744. },
  745. {
  746. Source: secretName,
  747. Target: targetName2,
  748. },
  749. },
  750. }
  751. _, err := service.getAciSecretsVolumeMounts()
  752. assert.Equal(t, err.Error(),
  753. fmt.Sprintf(`mount paths %q and %q collide. A volume mount cannot include another one.`,
  754. path.Dir(targetName1), path.Dir(targetName2)))
  755. })
  756. t.Run("convert colliding absolute targets", func(t *testing.T) {
  757. targetName1 := path.Join(absBasePath, "target1")
  758. targetName2 := path.Join(absBasePath, "sub/folder/target2")
  759. service := serviceConfigAciHelper{
  760. Name: serviceName,
  761. Secrets: []types.ServiceSecretConfig{
  762. {
  763. Source: secretName,
  764. Target: targetName1,
  765. },
  766. {
  767. Source: secretName,
  768. Target: targetName2,
  769. },
  770. },
  771. }
  772. _, err := service.getAciSecretsVolumeMounts()
  773. assert.Equal(t, err.Error(),
  774. fmt.Sprintf(`mount paths %q and %q collide. A volume mount cannot include another one.`,
  775. path.Dir(targetName1), path.Dir(targetName2)))
  776. })
  777. }
  778. func container(status *string) containerinstance.Container {
  779. var state *containerinstance.ContainerState = nil
  780. if status != nil {
  781. state = &containerinstance.ContainerState{
  782. State: status,
  783. }
  784. }
  785. return containerinstance.Container{
  786. ContainerProperties: &containerinstance.ContainerProperties{
  787. InstanceView: &containerinstance.ContainerPropertiesInstanceView{
  788. CurrentState: state,
  789. },
  790. },
  791. }
  792. }
  793. func group(status *string) containerinstance.ContainerGroup {
  794. var view *containerinstance.ContainerGroupPropertiesInstanceView = nil
  795. if status != nil {
  796. view = &containerinstance.ContainerGroupPropertiesInstanceView{
  797. State: status,
  798. }
  799. }
  800. return containerinstance.ContainerGroup{
  801. ContainerGroupProperties: &containerinstance.ContainerGroupProperties{
  802. InstanceView: view,
  803. },
  804. }
  805. }