test_deps.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. import json
  2. import pytest
  3. from render import Render
  4. @pytest.fixture
  5. def mock_values():
  6. return {
  7. "images": {
  8. "test_image": {
  9. "repository": "nginx",
  10. "tag": "latest",
  11. }
  12. },
  13. }
  14. def test_add_postgres_missing_config(mock_values):
  15. render = Render(mock_values)
  16. c1 = render.add_container("test_container", "test_image")
  17. c1.healthcheck.disable()
  18. with pytest.raises(Exception):
  19. render.deps.postgres(
  20. "pg_container",
  21. "test_image",
  22. {"user": "test_user", "password": "test_password", "database": "test_database"}, # type: ignore
  23. )
  24. def test_add_postgres_unsupported_repo(mock_values):
  25. mock_values["images"]["pg_image"] = {"repository": "unsupported_repo", "tag": "16"}
  26. render = Render(mock_values)
  27. c1 = render.add_container("test_container", "test_image")
  28. c1.healthcheck.disable()
  29. perms_container = render.deps.perms("perms_container")
  30. with pytest.raises(Exception):
  31. render.deps.postgres(
  32. "pg_container",
  33. "pg_image",
  34. {
  35. "user": "test_user",
  36. "password": "test_@password",
  37. "database": "test_database",
  38. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  39. },
  40. perms_container,
  41. )
  42. def test_add_postgres(mock_values):
  43. mock_values["images"]["pg_image"] = {"repository": "postgres", "tag": "16"}
  44. render = Render(mock_values)
  45. c1 = render.add_container("test_container", "test_image")
  46. c1.healthcheck.disable()
  47. perms_container = render.deps.perms("perms_container")
  48. p = render.deps.postgres(
  49. "pg_container",
  50. "pg_image",
  51. {
  52. "user": "test_user",
  53. "password": "test_@password",
  54. "database": "test_database",
  55. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  56. },
  57. perms_container,
  58. )
  59. if perms_container.has_actions():
  60. perms_container.activate()
  61. p.container.depends.add_dependency("perms_container", "service_completed_successfully")
  62. output = render.render()
  63. assert (
  64. p.get_url("postgres") == "postgres://test_user:test_%40password@pg_container:5432/test_database?sslmode=disable"
  65. )
  66. assert "devices" not in output["services"]["pg_container"]
  67. assert "reservations" not in output["services"]["pg_container"]["deploy"]["resources"]
  68. assert output["services"]["pg_container"]["image"] == "postgres:16"
  69. assert output["services"]["pg_container"]["user"] == "999:999"
  70. assert output["services"]["pg_container"]["deploy"]["resources"]["limits"]["cpus"] == "2.0"
  71. assert output["services"]["pg_container"]["deploy"]["resources"]["limits"]["memory"] == "4096M"
  72. assert output["services"]["pg_container"]["healthcheck"] == {
  73. "test": [
  74. "CMD",
  75. "pg_isready",
  76. "-h",
  77. "127.0.0.1",
  78. "-p",
  79. "5432",
  80. "-U",
  81. "test_user",
  82. "-d",
  83. "test_database",
  84. ],
  85. "interval": "30s",
  86. "timeout": "5s",
  87. "retries": 5,
  88. "start_period": "15s",
  89. "start_interval": "2s",
  90. }
  91. assert output["services"]["pg_container"]["volumes"] == [
  92. {
  93. "type": "volume",
  94. "source": "test_volume",
  95. "target": "/var/lib/postgresql/data",
  96. "read_only": False,
  97. "volume": {"nocopy": False},
  98. }
  99. ]
  100. assert output["services"]["pg_container"]["environment"] == {
  101. "TZ": "Etc/UTC",
  102. "UMASK": "002",
  103. "UMASK_SET": "002",
  104. "NVIDIA_VISIBLE_DEVICES": "void",
  105. "POSTGRES_USER": "test_user",
  106. "POSTGRES_PASSWORD": "test_@password",
  107. "POSTGRES_DB": "test_database",
  108. "PGPORT": "5432",
  109. }
  110. assert output["services"]["pg_container"]["depends_on"] == {
  111. "perms_container": {"condition": "service_completed_successfully"},
  112. "pg_container_upgrade": {"condition": "service_completed_successfully"},
  113. }
  114. assert output["services"]["perms_container"]["restart"] == "on-failure:1"
  115. def test_add_redis_missing_config(mock_values):
  116. render = Render(mock_values)
  117. c1 = render.add_container("test_container", "test_image")
  118. c1.healthcheck.disable()
  119. with pytest.raises(Exception):
  120. render.deps.redis(
  121. "redis_container",
  122. "test_image",
  123. {"password": "test_password", "volume": {}}, # type: ignore
  124. )
  125. def test_add_redis_unsupported_repo(mock_values):
  126. mock_values["images"]["redis_image"] = {"repository": "unsupported_repo", "tag": "latest"}
  127. render = Render(mock_values)
  128. c1 = render.add_container("test_container", "test_image")
  129. c1.healthcheck.disable()
  130. perms_container = render.deps.perms("perms_container")
  131. with pytest.raises(Exception):
  132. render.deps.redis(
  133. "redis_container",
  134. "redis_image",
  135. {
  136. "password": "test&password@",
  137. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  138. },
  139. perms_container,
  140. )
  141. def test_add_redis_with_password_with_spaces(mock_values):
  142. mock_values["images"]["redis_image"] = {"repository": "redis", "tag": "latest"}
  143. render = Render(mock_values)
  144. c1 = render.add_container("test_container", "test_image")
  145. c1.healthcheck.disable()
  146. with pytest.raises(Exception):
  147. render.deps.redis(
  148. "redis_container",
  149. "redis_image",
  150. {"password": "test password", "volume": {}}, # type: ignore
  151. )
  152. def test_add_redis(mock_values):
  153. mock_values["images"]["redis_image"] = {"repository": "valkey/valkey", "tag": "latest"}
  154. mock_values["run_as"] = {"user": 0, "group": 0}
  155. render = Render(mock_values)
  156. c1 = render.add_container("test_container", "test_image")
  157. c1.healthcheck.disable()
  158. perms_container = render.deps.perms("perms_container")
  159. r = render.deps.redis(
  160. "redis_container",
  161. "redis_image",
  162. {
  163. "password": "test&password@",
  164. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  165. },
  166. perms_container,
  167. )
  168. c1.environment.add_env("REDIS_URL", r.get_url("redis"))
  169. if perms_container.has_actions():
  170. perms_container.activate()
  171. r.container.depends.add_dependency("perms_container", "service_completed_successfully")
  172. output = render.render()
  173. assert "devices" not in output["services"]["redis_container"]
  174. assert "reservations" not in output["services"]["redis_container"]["deploy"]["resources"]
  175. assert (
  176. output["services"]["test_container"]["environment"]["REDIS_URL"]
  177. == "redis://default:test%26password%40@redis_container:6379"
  178. )
  179. assert output["services"]["redis_container"]["image"] == "valkey/valkey:latest"
  180. assert output["services"]["redis_container"]["user"] == "568:568"
  181. assert output["services"]["redis_container"]["deploy"]["resources"]["limits"]["cpus"] == "2.0"
  182. assert output["services"]["redis_container"]["deploy"]["resources"]["limits"]["memory"] == "4096M"
  183. assert output["services"]["redis_container"]["healthcheck"] == {
  184. "test": [
  185. "CMD",
  186. "redis-cli",
  187. "-h",
  188. "127.0.0.1",
  189. "-p",
  190. "6379",
  191. "-a",
  192. "test&password@",
  193. "ping",
  194. ],
  195. "interval": "30s",
  196. "timeout": "5s",
  197. "retries": 5,
  198. "start_period": "15s",
  199. "start_interval": "2s",
  200. }
  201. assert output["services"]["redis_container"]["volumes"] == [
  202. {
  203. "type": "volume",
  204. "source": "test_volume",
  205. "target": "/data",
  206. "read_only": False,
  207. "volume": {"nocopy": False},
  208. }
  209. ]
  210. assert output["services"]["redis_container"]["environment"] == {
  211. "TZ": "Etc/UTC",
  212. "UMASK": "002",
  213. "UMASK_SET": "002",
  214. "NVIDIA_VISIBLE_DEVICES": "void",
  215. "REDIS_PASSWORD": "test&password@",
  216. }
  217. assert output["services"]["redis_container"]["depends_on"] == {
  218. "perms_container": {"condition": "service_completed_successfully"}
  219. }
  220. def test_add_mariadb_missing_config(mock_values):
  221. render = Render(mock_values)
  222. c1 = render.add_container("test_container", "test_image")
  223. c1.healthcheck.disable()
  224. with pytest.raises(Exception):
  225. render.deps.mariadb(
  226. "mariadb_container",
  227. "test_image",
  228. {"user": "test_user", "password": "test_password", "database": "test_database"}, # type: ignore
  229. )
  230. def test_add_mariadb_unsupported_repo(mock_values):
  231. mock_values["images"]["mariadb_image"] = {"repository": "unsupported_repo", "tag": "latest"}
  232. render = Render(mock_values)
  233. c1 = render.add_container("test_container", "test_image")
  234. c1.healthcheck.disable()
  235. perms_container = render.deps.perms("perms_container")
  236. with pytest.raises(Exception):
  237. render.deps.mariadb(
  238. "mariadb_container",
  239. "mariadb_image",
  240. {
  241. "user": "test_user",
  242. "password": "test_password",
  243. "database": "test_database",
  244. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  245. },
  246. perms_container,
  247. )
  248. def test_add_mariadb(mock_values):
  249. mock_values["images"]["mariadb_image"] = {"repository": "mariadb", "tag": "latest"}
  250. render = Render(mock_values)
  251. c1 = render.add_container("test_container", "test_image")
  252. c1.healthcheck.disable()
  253. perms_container = render.deps.perms("perms_container")
  254. m = render.deps.mariadb(
  255. "mariadb_container",
  256. "mariadb_image",
  257. {
  258. "user": "test_user",
  259. "password": "test_password",
  260. "database": "test_database",
  261. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  262. },
  263. perms_container,
  264. )
  265. if perms_container.has_actions():
  266. perms_container.activate()
  267. m.container.depends.add_dependency("perms_container", "service_completed_successfully")
  268. output = render.render()
  269. assert "devices" not in output["services"]["mariadb_container"]
  270. assert "reservations" not in output["services"]["mariadb_container"]["deploy"]["resources"]
  271. assert output["services"]["mariadb_container"]["image"] == "mariadb:latest"
  272. assert output["services"]["mariadb_container"]["user"] == "999:999"
  273. assert output["services"]["mariadb_container"]["deploy"]["resources"]["limits"]["cpus"] == "2.0"
  274. assert output["services"]["mariadb_container"]["deploy"]["resources"]["limits"]["memory"] == "4096M"
  275. assert output["services"]["mariadb_container"]["healthcheck"] == {
  276. "test": [
  277. "CMD",
  278. "mariadb-admin",
  279. "--user=root",
  280. "--host=127.0.0.1",
  281. "--port=3306",
  282. "--password=test_password",
  283. "ping",
  284. ],
  285. "interval": "30s",
  286. "timeout": "5s",
  287. "retries": 5,
  288. "start_period": "15s",
  289. "start_interval": "2s",
  290. }
  291. assert output["services"]["mariadb_container"]["volumes"] == [
  292. {
  293. "type": "volume",
  294. "source": "test_volume",
  295. "target": "/var/lib/mysql",
  296. "read_only": False,
  297. "volume": {"nocopy": False},
  298. }
  299. ]
  300. assert output["services"]["mariadb_container"]["environment"] == {
  301. "TZ": "Etc/UTC",
  302. "UMASK": "002",
  303. "UMASK_SET": "002",
  304. "NVIDIA_VISIBLE_DEVICES": "void",
  305. "MARIADB_USER": "test_user",
  306. "MARIADB_PASSWORD": "test_password",
  307. "MARIADB_ROOT_PASSWORD": "test_password",
  308. "MARIADB_DATABASE": "test_database",
  309. "MARIADB_AUTO_UPGRADE": "true",
  310. }
  311. assert output["services"]["mariadb_container"]["depends_on"] == {
  312. "perms_container": {"condition": "service_completed_successfully"}
  313. }
  314. def test_add_perms_container(mock_values):
  315. mock_values["ix_volumes"] = {
  316. "test_dataset1": "/mnt/test/1",
  317. "test_dataset2": "/mnt/test/2",
  318. "test_dataset3": "/mnt/test/3",
  319. }
  320. mock_values["images"]["postgres_image"] = {"repository": "postgres", "tag": "17"}
  321. mock_values["images"]["redis_image"] = {"repository": "valkey/valkey", "tag": "latest"}
  322. mock_values["images"]["mariadb_image"] = {"repository": "mariadb", "tag": "latest"}
  323. render = Render(mock_values)
  324. c1 = render.add_container("test_container", "test_image")
  325. c1.healthcheck.disable()
  326. # fmt: off
  327. volume_perms = {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}}
  328. volume_no_perms = {"type": "volume", "volume_config": {"volume_name": "test_volume"}}
  329. host_path_perms = {"type": "host_path", "host_path_config": {"path": "/mnt/test", "auto_permissions": True}}
  330. host_path_no_perms = {"type": "host_path", "host_path_config": {"path": "/mnt/test"}}
  331. host_path_acl_perms = {"type": "host_path", "host_path_config": {"acl": {"path": "/mnt/test"}, "acl_enable": True, "auto_permissions": True}} # noqa
  332. ix_volume_no_perms = {"type": "ix_volume", "ix_volume_config": {"dataset_name": "test_dataset1"}}
  333. ix_volume_perms = {"type": "ix_volume", "ix_volume_config": {"dataset_name": "test_dataset2", "auto_permissions": True}} # noqa
  334. ix_volume_acl_perms = {"type": "ix_volume", "ix_volume_config": {"dataset_name": "test_dataset3", "acl_enable": True, "auto_permissions": True}} # noqa
  335. temp_volume = {"type": "temporary", "volume_config": {"volume_name": "test_temp_volume"}}
  336. read_only_volume = {"type": "volume", "read_only": True, "volume_config": {"volume_name": "test_read_only_volume", "auto_permissions": True}} # noqa
  337. # fmt: on
  338. c1.add_storage("/some/path1", volume_perms)
  339. c1.add_storage("/some/path2", volume_no_perms)
  340. c1.add_storage("/some/path3", host_path_perms)
  341. c1.add_storage("/some/path4", host_path_no_perms)
  342. c1.add_storage("/some/path5", host_path_acl_perms)
  343. c1.add_storage("/some/path6", ix_volume_no_perms)
  344. c1.add_storage("/some/path7", ix_volume_perms)
  345. c1.add_storage("/some/path8", ix_volume_acl_perms)
  346. c1.add_storage("/some/path9", temp_volume)
  347. c1.add_storage("/some/path10", read_only_volume)
  348. perms_container = render.deps.perms("test_perms_container")
  349. perms_container.add_or_skip_action("data", volume_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  350. perms_container.add_or_skip_action("data2", volume_no_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  351. perms_container.add_or_skip_action("data3", host_path_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  352. perms_container.add_or_skip_action("data4", host_path_no_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  353. perms_container.add_or_skip_action("data5", host_path_acl_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  354. perms_container.add_or_skip_action("data6", ix_volume_no_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  355. perms_container.add_or_skip_action("data7", ix_volume_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  356. perms_container.add_or_skip_action("data8", ix_volume_acl_perms, {"uid": 1000, "gid": 1000, "mode": "check"})
  357. perms_container.add_or_skip_action("data9", temp_volume, {"uid": 1000, "gid": 1000, "mode": "check"})
  358. perms_container.add_or_skip_action("data10", read_only_volume, {"uid": 1000, "gid": 1000, "mode": "check"})
  359. postgres = render.deps.postgres(
  360. "postgres_container",
  361. "postgres_image",
  362. {
  363. "user": "test_user",
  364. "password": "test_password",
  365. "database": "test_database",
  366. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  367. },
  368. perms_container,
  369. )
  370. redis = render.deps.redis(
  371. "redis_container",
  372. "redis_image",
  373. {
  374. "password": "test_password",
  375. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  376. },
  377. perms_container,
  378. )
  379. mariadb = render.deps.mariadb(
  380. "mariadb_container",
  381. "mariadb_image",
  382. {
  383. "user": "test_user",
  384. "password": "test_password",
  385. "database": "test_database",
  386. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  387. },
  388. perms_container,
  389. )
  390. if perms_container.has_actions():
  391. perms_container.activate()
  392. c1.depends.add_dependency("test_perms_container", "service_completed_successfully")
  393. postgres.container.depends.add_dependency("test_perms_container", "service_completed_successfully")
  394. redis.container.depends.add_dependency("test_perms_container", "service_completed_successfully")
  395. mariadb.container.depends.add_dependency("test_perms_container", "service_completed_successfully")
  396. output = render.render()
  397. assert output["services"]["test_perms_container"]["network_mode"] == "none"
  398. assert output["services"]["test_container"]["depends_on"] == {
  399. "test_perms_container": {"condition": "service_completed_successfully"}
  400. }
  401. assert output["configs"]["permissions_run_script"]["content"] != ""
  402. # fmt: off
  403. content = [
  404. {"read_only": False, "mount_path": "/mnt/permission/data", "is_temporary": False, "identifier": "data", "recursive": False, "mode": "check", "uid": 1000, "gid": 1000, "chmod": None}, # noqa
  405. {"read_only": False, "mount_path": "/mnt/permission/data3", "is_temporary": False, "identifier": "data3", "recursive": False, "mode": "check", "uid": 1000, "gid": 1000, "chmod": None}, # noqa
  406. {"read_only": False, "mount_path": "/mnt/permission/data6", "is_temporary": False, "identifier": "data6", "recursive": False, "mode": "check", "uid": 1000, "gid": 1000, "chmod": None}, # noqa
  407. {"read_only": False, "mount_path": "/mnt/permission/data7", "is_temporary": False, "identifier": "data7", "recursive": False, "mode": "check", "uid": 1000, "gid": 1000, "chmod": None}, # noqa
  408. {"read_only": False, "mount_path": "/mnt/permission/data9", "is_temporary": True, "identifier": "data9", "recursive": True, "mode": "check", "uid": 1000, "gid": 1000, "chmod": None}, # noqa
  409. {"read_only": True, "mount_path": "/mnt/permission/data10", "is_temporary": False, "identifier": "data10", "recursive": False, "mode": "check", "uid": 1000, "gid": 1000, "chmod": None}, # noqa
  410. {"read_only": False, "mount_path": "/mnt/permission/postgres_container_postgres_data", "is_temporary": False, "identifier": "postgres_container_postgres_data", "recursive": False, "mode": "check", "uid": 999, "gid": 999, "chmod": None}, # noqa
  411. {"read_only": False, "mount_path": "/mnt/permission/redis_container_redis_data", "is_temporary": False, "identifier": "redis_container_redis_data", "recursive": False, "mode": "check", "uid": 568, "gid": 568, "chmod": None}, # noqa
  412. {"read_only": False, "mount_path": "/mnt/permission/mariadb_container_mariadb_data", "is_temporary": False, "identifier": "mariadb_container_mariadb_data", "recursive": False, "mode": "check", "uid": 999, "gid": 999, "chmod": None}, # noqa
  413. ]
  414. # fmt: on
  415. assert output["configs"]["permissions_actions_data"]["content"] == json.dumps(content)
  416. def test_add_duplicate_perms_action(mock_values):
  417. render = Render(mock_values)
  418. c1 = render.add_container("test_container", "test_image")
  419. c1.healthcheck.disable()
  420. vol_config = {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}}
  421. c1.add_storage("/some/path", vol_config)
  422. perms_container = render.deps.perms("test_perms_container")
  423. perms_container.add_or_skip_action("data", vol_config, {"uid": 1000, "gid": 1000, "mode": "check"})
  424. with pytest.raises(Exception):
  425. perms_container.add_or_skip_action("data", vol_config, {"uid": 1000, "gid": 1000, "mode": "check"})
  426. def test_add_perm_action_without_auto_perms_enabled(mock_values):
  427. render = Render(mock_values)
  428. c1 = render.add_container("test_container", "test_image")
  429. c1.healthcheck.disable()
  430. vol_config = {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": False}}
  431. c1.add_storage("/some/path", vol_config)
  432. perms_container = render.deps.perms("test_perms_container")
  433. perms_container.add_or_skip_action("data", vol_config, {"uid": 1000, "gid": 1000, "mode": "check"})
  434. if perms_container.has_actions():
  435. perms_container.activate()
  436. c1.depends.add_dependency("test_perms_container", "service_completed_successfully")
  437. output = render.render()
  438. assert "configs" not in output
  439. assert "ix-test_perms_container" not in output["services"]
  440. assert "depends_on" not in output["services"]["test_container"]
  441. def test_add_unsupported_postgres_version(mock_values):
  442. mock_values["images"]["pg_image"] = {"repository": "postgres", "tag": "99"}
  443. render = Render(mock_values)
  444. c1 = render.add_container("test_container", "test_image")
  445. c1.healthcheck.disable()
  446. with pytest.raises(Exception):
  447. render.deps.postgres(
  448. "test_container",
  449. "test_image",
  450. {"user": "test_user", "password": "test_password", "database": "test_database"}, # type: ignore
  451. )
  452. def test_add_postgres_with_invalid_tag(mock_values):
  453. mock_values["images"]["pg_image"] = {"repository": "postgres", "tag": "latest"}
  454. render = Render(mock_values)
  455. c1 = render.add_container("test_container", "test_image")
  456. c1.healthcheck.disable()
  457. with pytest.raises(Exception):
  458. render.deps.postgres(
  459. "pg_container",
  460. "pg_image",
  461. {"user": "test_user", "password": "test_password", "database": "test_database"}, # type: ignore
  462. )
  463. def test_no_upgrade_container_with_non_postgres_image(mock_values):
  464. mock_values["images"]["postgres_image"] = {"repository": "tensorchord/pgvecto-rs", "tag": "pg15-v0.2.0"}
  465. render = Render(mock_values)
  466. c1 = render.add_container("test_container", "test_image")
  467. c1.healthcheck.disable()
  468. perms_container = render.deps.perms("test_perms_container")
  469. pg = render.deps.postgres(
  470. "postgres_container",
  471. "postgres_image",
  472. {
  473. "user": "test_user",
  474. "password": "test_password",
  475. "database": "test_database",
  476. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  477. },
  478. perms_container,
  479. )
  480. if perms_container.has_actions():
  481. perms_container.activate()
  482. pg.add_dependency("test_perms_container", "service_completed_successfully")
  483. output = render.render()
  484. assert len(output["services"]) == 3 # c1, pg, perms
  485. assert output["services"]["postgres_container"]["depends_on"] == {
  486. "test_perms_container": {"condition": "service_completed_successfully"}
  487. }
  488. def test_postgres_with_upgrade_container(mock_values):
  489. mock_values["images"]["pg_image"] = {"repository": "postgres", "tag": 16.6}
  490. render = Render(mock_values)
  491. c1 = render.add_container("test_container", "test_image")
  492. c1.healthcheck.disable()
  493. perms_container = render.deps.perms("test_perms_container")
  494. pg = render.deps.postgres(
  495. "postgres_container",
  496. "pg_image",
  497. {
  498. "user": "test_user",
  499. "password": "test_password",
  500. "database": "test_database",
  501. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  502. },
  503. perms_container,
  504. )
  505. if perms_container.has_actions():
  506. perms_container.activate()
  507. pg.add_dependency("test_perms_container", "service_completed_successfully")
  508. output = render.render()
  509. pg = output["services"]["postgres_container"]
  510. pgup = output["services"]["postgres_container_upgrade"]
  511. assert pg["volumes"] == pgup["volumes"]
  512. assert pg["user"] == pgup["user"]
  513. assert pgup["environment"]["TARGET_VERSION"] == "16"
  514. assert pgup["environment"]["DATA_DIR"] == "/var/lib/postgresql/data"
  515. pgup_env = pgup["environment"]
  516. pgup_env.pop("TARGET_VERSION")
  517. pgup_env.pop("DATA_DIR")
  518. assert pg["environment"] == pgup_env
  519. assert pg["depends_on"] == {
  520. "test_perms_container": {"condition": "service_completed_successfully"},
  521. "postgres_container_upgrade": {"condition": "service_completed_successfully"},
  522. }
  523. assert pgup["depends_on"] == {"test_perms_container": {"condition": "service_completed_successfully"}}
  524. assert pgup["restart"] == "on-failure:1"
  525. assert pgup["healthcheck"] == {"disable": True}
  526. assert pgup["image"] == "ixsystems/postgres-upgrade:1.0.1"
  527. assert pgup["entrypoint"] == ["/bin/bash", "-c", "/upgrade.sh"]
  528. def test_add_mongodb(mock_values):
  529. mock_values["images"]["mongodb_image"] = {"repository": "mongodb", "tag": "latest"}
  530. render = Render(mock_values)
  531. c1 = render.add_container("test_container", "test_image")
  532. c1.healthcheck.disable()
  533. perms_container = render.deps.perms("perms_container")
  534. m = render.deps.mongodb(
  535. "mongodb_container",
  536. "mongodb_image",
  537. {
  538. "user": "test_user",
  539. "password": "test_password",
  540. "database": "test_database",
  541. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  542. },
  543. perms_container,
  544. )
  545. if perms_container.has_actions():
  546. perms_container.activate()
  547. m.container.depends.add_dependency("perms_container", "service_completed_successfully")
  548. output = render.render()
  549. assert "devices" not in output["services"]["mongodb_container"]
  550. assert "reservations" not in output["services"]["mongodb_container"]["deploy"]["resources"]
  551. assert output["services"]["mongodb_container"]["image"] == "mongodb:latest"
  552. assert output["services"]["mongodb_container"]["user"] == "999:999"
  553. assert output["services"]["mongodb_container"]["deploy"]["resources"]["limits"]["cpus"] == "2.0"
  554. assert output["services"]["mongodb_container"]["deploy"]["resources"]["limits"]["memory"] == "4096M"
  555. assert output["services"]["mongodb_container"]["healthcheck"] == {
  556. "test": [
  557. "CMD",
  558. "mongosh",
  559. "--host",
  560. "127.0.0.1",
  561. "--port",
  562. "27017",
  563. "test_database",
  564. "--eval",
  565. 'db.adminCommand("ping")',
  566. "--quiet",
  567. ],
  568. "interval": "30s",
  569. "timeout": "5s",
  570. "retries": 5,
  571. "start_period": "15s",
  572. "start_interval": "2s",
  573. }
  574. assert output["services"]["mongodb_container"]["volumes"] == [
  575. {
  576. "type": "volume",
  577. "source": "test_volume",
  578. "target": "/data/db",
  579. "read_only": False,
  580. "volume": {"nocopy": False},
  581. }
  582. ]
  583. assert output["services"]["mongodb_container"]["environment"] == {
  584. "TZ": "Etc/UTC",
  585. "UMASK": "002",
  586. "UMASK_SET": "002",
  587. "NVIDIA_VISIBLE_DEVICES": "void",
  588. "MONGO_INITDB_ROOT_USERNAME": "test_user",
  589. "MONGO_INITDB_ROOT_PASSWORD": "test_password",
  590. "MONGO_INITDB_DATABASE": "test_database",
  591. }
  592. assert output["services"]["mongodb_container"]["depends_on"] == {
  593. "perms_container": {"condition": "service_completed_successfully"}
  594. }
  595. def test_add_mongodb_unsupported_repo(mock_values):
  596. mock_values["images"]["mongo_image"] = {"repository": "unsupported_repo", "tag": "7"}
  597. render = Render(mock_values)
  598. c1 = render.add_container("test_container", "test_image")
  599. c1.healthcheck.disable()
  600. perms_container = render.deps.perms("perms_container")
  601. with pytest.raises(Exception):
  602. render.deps.mongodb(
  603. "mongo_container",
  604. "mongo_image",
  605. {
  606. "user": "test_user",
  607. "password": "test_@password",
  608. "database": "test_database",
  609. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  610. },
  611. perms_container,
  612. )
  613. def test_add_meilisearch(mock_values):
  614. mock_values["images"]["meili_image"] = {"repository": "getmeili/meilisearch", "tag": "v1.17.0"}
  615. render = Render(mock_values)
  616. c1 = render.add_container("test_container", "test_image")
  617. c1.healthcheck.disable()
  618. perms_container = render.deps.perms("perms_container")
  619. m = render.deps.meilisearch(
  620. "meili_container",
  621. "meili_image",
  622. {
  623. "master_key": "test_master_key",
  624. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  625. },
  626. perms_container,
  627. )
  628. if perms_container.has_actions():
  629. perms_container.activate()
  630. m.container.depends.add_dependency("perms_container", "service_completed_successfully")
  631. output = render.render()
  632. assert "devices" not in output["services"]["meili_container"]
  633. assert "reservations" not in output["services"]["meili_container"]["deploy"]["resources"]
  634. assert output["services"]["meili_container"]["image"] == "getmeili/meilisearch:v1.17.0"
  635. assert output["services"]["meili_container"]["user"] == "568:568"
  636. assert output["services"]["meili_container"]["deploy"]["resources"]["limits"]["cpus"] == "2.0"
  637. assert output["services"]["meili_container"]["deploy"]["resources"]["limits"]["memory"] == "4096M"
  638. assert output["services"]["meili_container"]["healthcheck"] == {
  639. "test": [
  640. "CMD",
  641. "curl",
  642. "--request",
  643. "GET",
  644. "--silent",
  645. "--output",
  646. "/dev/null",
  647. "--show-error",
  648. "--fail",
  649. "http://127.0.0.1:7700/health",
  650. ],
  651. "interval": "30s",
  652. "timeout": "5s",
  653. "retries": 5,
  654. "start_period": "15s",
  655. "start_interval": "2s",
  656. }
  657. assert output["services"]["meili_container"]["volumes"] == [
  658. {
  659. "type": "volume",
  660. "source": "test_volume",
  661. "target": "/meili_data",
  662. "read_only": False,
  663. "volume": {"nocopy": False},
  664. }
  665. ]
  666. assert output["services"]["meili_container"]["environment"] == {
  667. "TZ": "Etc/UTC",
  668. "UMASK": "002",
  669. "UMASK_SET": "002",
  670. "NVIDIA_VISIBLE_DEVICES": "void",
  671. "MEILI_MASTER_KEY": "test_master_key",
  672. "MEILI_HTTP_ADDR": "0.0.0.0:7700",
  673. "MEILI_NO_ANALYTICS": "true",
  674. "MEILI_EXPERIMENTAL_DUMPLESS_UPGRADE": "true",
  675. }
  676. assert output["services"]["meili_container"]["depends_on"] == {
  677. "perms_container": {"condition": "service_completed_successfully"}
  678. }
  679. def test_add_meilisearch_unsupported_repo(mock_values):
  680. mock_values["images"]["meili_image"] = {"repository": "unsupported_repo", "tag": "7"}
  681. render = Render(mock_values)
  682. c1 = render.add_container("test_container", "test_image")
  683. c1.healthcheck.disable()
  684. perms_container = render.deps.perms("perms_container")
  685. with pytest.raises(Exception):
  686. render.deps.meilisearch(
  687. "meili_container",
  688. "meili_image",
  689. {
  690. "master_key": "test_master_key",
  691. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  692. },
  693. perms_container,
  694. )
  695. def test_add_elasticsearch(mock_values):
  696. mock_values["images"]["elastic_image"] = {
  697. "repository": "docker.elastic.co/elasticsearch/elasticsearch",
  698. "tag": "9.1.2",
  699. }
  700. render = Render(mock_values)
  701. c1 = render.add_container("test_container", "test_image")
  702. c1.healthcheck.disable()
  703. perms_container = render.deps.perms("perms_container")
  704. m = render.deps.elasticsearch(
  705. "elastic_container",
  706. "elastic_image",
  707. {
  708. "password": "test_password",
  709. "node_name": "some_test_node",
  710. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  711. },
  712. perms_container,
  713. )
  714. if perms_container.has_actions():
  715. perms_container.activate()
  716. m.container.depends.add_dependency("perms_container", "service_completed_successfully")
  717. output = render.render()
  718. assert "devices" not in output["services"]["elastic_container"]
  719. assert "reservations" not in output["services"]["elastic_container"]["deploy"]["resources"]
  720. assert output["services"]["elastic_container"]["image"] == "docker.elastic.co/elasticsearch/elasticsearch:9.1.2"
  721. assert output["services"]["elastic_container"]["user"] == "1000:1000"
  722. assert output["services"]["elastic_container"]["deploy"]["resources"]["limits"]["cpus"] == "2.0"
  723. assert output["services"]["elastic_container"]["deploy"]["resources"]["limits"]["memory"] == "4096M"
  724. assert output["services"]["elastic_container"]["healthcheck"] == {
  725. "test": [
  726. "CMD",
  727. "curl",
  728. "--request",
  729. "GET",
  730. "--silent",
  731. "--output",
  732. "/dev/null",
  733. "--show-error",
  734. "--fail",
  735. "--header",
  736. "Authorization: Basic ZWxhc3RpYzp0ZXN0X3Bhc3N3b3Jk",
  737. "http://127.0.0.1:9200/_cluster/health?local=true",
  738. ], # noqa
  739. "interval": "30s",
  740. "timeout": "5s",
  741. "retries": 5,
  742. "start_period": "15s",
  743. "start_interval": "2s",
  744. }
  745. assert output["services"]["elastic_container"]["volumes"] == [
  746. {
  747. "type": "volume",
  748. "source": "test_volume",
  749. "target": "/usr/share/elasticsearch/data",
  750. "read_only": False,
  751. "volume": {"nocopy": False},
  752. }
  753. ]
  754. assert output["services"]["elastic_container"]["environment"] == {
  755. "TZ": "Etc/UTC",
  756. "UMASK": "002",
  757. "UMASK_SET": "002",
  758. "NVIDIA_VISIBLE_DEVICES": "void",
  759. "ELASTIC_PASSWORD": "test_password",
  760. "http.port": "9200",
  761. "path.data": "/usr/share/elasticsearch/data",
  762. "path.repo": "/usr/share/elasticsearch/data/snapshots",
  763. "node.name": "some_test_node",
  764. "discovery.type": "single-node",
  765. "xpack.security.enabled": "true",
  766. "xpack.security.transport.ssl.enabled": "false",
  767. }
  768. assert output["services"]["elastic_container"]["depends_on"] == {
  769. "perms_container": {"condition": "service_completed_successfully"}
  770. }
  771. def test_add_elasticsearch_unsupported_repo(mock_values):
  772. mock_values["images"]["elastic_image"] = {"repository": "unsupported_repo", "tag": "7"}
  773. render = Render(mock_values)
  774. c1 = render.add_container("test_container", "test_image")
  775. c1.healthcheck.disable()
  776. perms_container = render.deps.perms("perms_container")
  777. with pytest.raises(Exception):
  778. render.deps.elasticsearch(
  779. "elastic_container",
  780. "elastic_image",
  781. {
  782. "password": "test_password",
  783. "node_name": "some_test_node",
  784. "volume": {"type": "volume", "volume_config": {"volume_name": "test_volume", "auto_permissions": True}},
  785. },
  786. perms_container,
  787. )