uffizzi-core 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +52 -0
  4. data/Rakefile +31 -0
  5. data/app/assets/config/uffizzi_core_manifest.js +1 -0
  6. data/app/assets/stylesheets/uffizzi_core/application.css +15 -0
  7. data/app/clients/uffizzi_core/amazon_registry_client.rb +18 -0
  8. data/app/clients/uffizzi_core/azure_registry_client/request_result.rb +5 -0
  9. data/app/clients/uffizzi_core/azure_registry_client.rb +42 -0
  10. data/app/clients/uffizzi_core/controller_client/request_result.rb +5 -0
  11. data/app/clients/uffizzi_core/controller_client.rb +106 -0
  12. data/app/clients/uffizzi_core/docker_hub_client/request_result.rb +7 -0
  13. data/app/clients/uffizzi_core/docker_hub_client.rb +141 -0
  14. data/app/clients/uffizzi_core/github_container_registry_client/request_result.rb +7 -0
  15. data/app/clients/uffizzi_core/github_container_registry_client.rb +52 -0
  16. data/app/clients/uffizzi_core/google_registry_client/request_result.rb +5 -0
  17. data/app/clients/uffizzi_core/google_registry_client.rb +42 -0
  18. data/app/contexts/uffizzi_core/base_context.rb +12 -0
  19. data/app/contexts/uffizzi_core/project_context.rb +13 -0
  20. data/app/contexts/uffizzi_core/webhooks_context.rb +9 -0
  21. data/app/controllers/concerns/uffizzi_core/auth_management.rb +23 -0
  22. data/app/controllers/concerns/uffizzi_core/authorization_concern.rb +38 -0
  23. data/app/controllers/concerns/uffizzi_core/dependency_injection_concern.rb +19 -0
  24. data/app/controllers/uffizzi_core/api/cli/v1/account/application_controller.rb +7 -0
  25. data/app/controllers/uffizzi_core/api/cli/v1/account/credentials_controller.rb +91 -0
  26. data/app/controllers/uffizzi_core/api/cli/v1/application_controller.rb +5 -0
  27. data/app/controllers/uffizzi_core/api/cli/v1/projects/application_controller.rb +11 -0
  28. data/app/controllers/uffizzi_core/api/cli/v1/projects/compose_files_controller.rb +93 -0
  29. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/activity_items_controller.rb +36 -0
  30. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/application_controller.rb +7 -0
  31. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/containers/application_controller.rb +8 -0
  32. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/containers/logs_controller.rb +27 -0
  33. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/containers_controller.rb +24 -0
  34. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/events_controller.rb +29 -0
  35. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments_controller.rb +179 -0
  36. data/app/controllers/uffizzi_core/api/cli/v1/projects/secrets_controller.rb +61 -0
  37. data/app/controllers/uffizzi_core/api/cli/v1/projects_controller.rb +85 -0
  38. data/app/controllers/uffizzi_core/api/cli/v1/sessions_controller.rb +43 -0
  39. data/app/controllers/uffizzi_core/application_controller.rb +57 -0
  40. data/app/errors/uffizzi_core/compose_file/build_error.rb +4 -0
  41. data/app/errors/uffizzi_core/compose_file/credential_error.rb +4 -0
  42. data/app/errors/uffizzi_core/compose_file/parse_error.rb +4 -0
  43. data/app/errors/uffizzi_core/compose_file/secrets_error.rb +4 -0
  44. data/app/errors/uffizzi_core/deployment/image_pull_error.rb +10 -0
  45. data/app/errors/uffizzi_core/deployment_not_found_error.rb +10 -0
  46. data/app/forms/uffizzi_core/api/cli/v1/account/credential/check_credential_form.rb +16 -0
  47. data/app/forms/uffizzi_core/api/cli/v1/account/credential/create_form.rb +26 -0
  48. data/app/forms/uffizzi_core/api/cli/v1/compose_file/check_credentials_form.rb +21 -0
  49. data/app/forms/uffizzi_core/api/cli/v1/compose_file/cli_form.rb +22 -0
  50. data/app/forms/uffizzi_core/api/cli/v1/compose_file/create_form.rb +13 -0
  51. data/app/forms/uffizzi_core/api/cli/v1/compose_file/template_form.rb +44 -0
  52. data/app/forms/uffizzi_core/api/cli/v1/compose_file/update_form.rb +9 -0
  53. data/app/forms/uffizzi_core/api/cli/v1/config_file/create_form.rb +11 -0
  54. data/app/forms/uffizzi_core/api/cli/v1/deployment/create_form.rb +92 -0
  55. data/app/forms/uffizzi_core/api/cli/v1/deployment/update_form.rb +90 -0
  56. data/app/forms/uffizzi_core/api/cli/v1/project/create_form.rb +7 -0
  57. data/app/forms/uffizzi_core/api/cli/v1/project/update_form.rb +10 -0
  58. data/app/forms/uffizzi_core/api/cli/v1/secret/bulk_assign_form.rb +39 -0
  59. data/app/forms/uffizzi_core/api/cli/v1/session_create_form.rb +29 -0
  60. data/app/forms/uffizzi_core/api/cli/v1/template/create_form.rb +65 -0
  61. data/app/forms/uffizzi_core/application_form.rb +11 -0
  62. data/app/forms/uffizzi_core/application_form_without_active_record.rb +17 -0
  63. data/app/forms/uffizzi_core/mass_assignment_control_concern.rb +22 -0
  64. data/app/helpers/uffizzi_core/application_helper.rb +6 -0
  65. data/app/jobs/uffizzi_core/account/create_credential_job.rb +10 -0
  66. data/app/jobs/uffizzi_core/activity_item/docker/update_digest_job.rb +11 -0
  67. data/app/jobs/uffizzi_core/application_job.rb +7 -0
  68. data/app/jobs/uffizzi_core/config_file/apply_job.rb +31 -0
  69. data/app/jobs/uffizzi_core/credential/docker_hub/create_webhook_job.rb +15 -0
  70. data/app/jobs/uffizzi_core/deployment/create_credential_job.rb +32 -0
  71. data/app/jobs/uffizzi_core/deployment/create_credentials_job.rb +17 -0
  72. data/app/jobs/uffizzi_core/deployment/create_job.rb +15 -0
  73. data/app/jobs/uffizzi_core/deployment/create_webhooks_job.rb +13 -0
  74. data/app/jobs/uffizzi_core/deployment/delete_credential_job.rb +13 -0
  75. data/app/jobs/uffizzi_core/deployment/delete_job.rb +11 -0
  76. data/app/jobs/uffizzi_core/deployment/deploy_containers_job.rb +27 -0
  77. data/app/jobs/uffizzi_core/deployment/manage_deploy_activity_item_job.rb +38 -0
  78. data/app/lib/uffizzi_core/concerns/models/activity_item.rb +39 -0
  79. data/app/lib/uffizzi_core/concerns/models/credential.rb +65 -0
  80. data/app/lib/uffizzi_core/concerns/models/repo.rb +33 -0
  81. data/app/lib/uffizzi_core/rbac/user_access_service.rb +45 -0
  82. data/app/mailers/uffizzi_core/application_mailer.rb +8 -0
  83. data/app/models/concerns/uffizzi_core/hashid_concern.rb +25 -0
  84. data/app/models/concerns/uffizzi_core/state_machine_concern.rb +16 -0
  85. data/app/models/uffizzi_core/account.rb +83 -0
  86. data/app/models/uffizzi_core/activity_item/docker.rb +4 -0
  87. data/app/models/uffizzi_core/activity_item/github.rb +4 -0
  88. data/app/models/uffizzi_core/activity_item/memory_limit.rb +4 -0
  89. data/app/models/uffizzi_core/activity_item.rb +53 -0
  90. data/app/models/uffizzi_core/application_record.rb +7 -0
  91. data/app/models/uffizzi_core/build.rb +39 -0
  92. data/app/models/uffizzi_core/comment.rb +16 -0
  93. data/app/models/uffizzi_core/compose_file.rb +57 -0
  94. data/app/models/uffizzi_core/config_file.rb +24 -0
  95. data/app/models/uffizzi_core/container.rb +100 -0
  96. data/app/models/uffizzi_core/container_config_file.rb +8 -0
  97. data/app/models/uffizzi_core/continuous_preview.rb +4 -0
  98. data/app/models/uffizzi_core/coupon.rb +5 -0
  99. data/app/models/uffizzi_core/credential/amazon.rb +4 -0
  100. data/app/models/uffizzi_core/credential/azure.rb +4 -0
  101. data/app/models/uffizzi_core/credential/docker_hub.rb +4 -0
  102. data/app/models/uffizzi_core/credential/github.rb +4 -0
  103. data/app/models/uffizzi_core/credential/github_container_registry.rb +4 -0
  104. data/app/models/uffizzi_core/credential/google.rb +4 -0
  105. data/app/models/uffizzi_core/credential.rb +61 -0
  106. data/app/models/uffizzi_core/database.rb +4 -0
  107. data/app/models/uffizzi_core/database_offering.rb +4 -0
  108. data/app/models/uffizzi_core/deployment.rb +86 -0
  109. data/app/models/uffizzi_core/event.rb +13 -0
  110. data/app/models/uffizzi_core/invitation.rb +27 -0
  111. data/app/models/uffizzi_core/membership.rb +16 -0
  112. data/app/models/uffizzi_core/payment.rb +11 -0
  113. data/app/models/uffizzi_core/price.rb +9 -0
  114. data/app/models/uffizzi_core/product.rb +11 -0
  115. data/app/models/uffizzi_core/project.rb +67 -0
  116. data/app/models/uffizzi_core/rating.rb +20 -0
  117. data/app/models/uffizzi_core/repo/amazon.rb +4 -0
  118. data/app/models/uffizzi_core/repo/azure.rb +4 -0
  119. data/app/models/uffizzi_core/repo/docker_hub.rb +4 -0
  120. data/app/models/uffizzi_core/repo/github.rb +4 -0
  121. data/app/models/uffizzi_core/repo/github_container_registry.rb +4 -0
  122. data/app/models/uffizzi_core/repo/google.rb +4 -0
  123. data/app/models/uffizzi_core/repo.rb +29 -0
  124. data/app/models/uffizzi_core/role.rb +17 -0
  125. data/app/models/uffizzi_core/secret.rb +9 -0
  126. data/app/models/uffizzi_core/template.rb +19 -0
  127. data/app/models/uffizzi_core/user.rb +62 -0
  128. data/app/models/uffizzi_core/user_project.rb +14 -0
  129. data/app/policies/uffizzi_core/api/cli/v1/account/credentials_policy.rb +19 -0
  130. data/app/policies/uffizzi_core/api/cli/v1/projects/compose_files_policy.rb +15 -0
  131. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments/activity_items_policy.rb +7 -0
  132. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments/containers_policy.rb +7 -0
  133. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments/events_policy.rb +7 -0
  134. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments_policy.rb +27 -0
  135. data/app/policies/uffizzi_core/api/cli/v1/projects/secrets_policy.rb +15 -0
  136. data/app/policies/uffizzi_core/api/cli/v1/projects_policy.rb +19 -0
  137. data/app/policies/uffizzi_core/application_policy.rb +12 -0
  138. data/app/repositories/uffizzi_core/activity_item_repo.rb +9 -0
  139. data/app/repositories/uffizzi_core/basic_order_repo.rb +11 -0
  140. data/app/repositories/uffizzi_core/build_repo.rb +23 -0
  141. data/app/repositories/uffizzi_core/comment_repo.rb +11 -0
  142. data/app/repositories/uffizzi_core/compose_file_repo.rb +11 -0
  143. data/app/repositories/uffizzi_core/config_file_repo.rb +40 -0
  144. data/app/repositories/uffizzi_core/container_repo.rb +25 -0
  145. data/app/repositories/uffizzi_core/credential_repo.rb +31 -0
  146. data/app/repositories/uffizzi_core/deployment_repo.rb +24 -0
  147. data/app/repositories/uffizzi_core/event_repo.rb +9 -0
  148. data/app/repositories/uffizzi_core/membership_repo.rb +10 -0
  149. data/app/repositories/uffizzi_core/price_repo.rb +11 -0
  150. data/app/repositories/uffizzi_core/product_repo.rb +11 -0
  151. data/app/repositories/uffizzi_core/project_repo.rb +10 -0
  152. data/app/repositories/uffizzi_core/repo_repo.rb +10 -0
  153. data/app/repositories/uffizzi_core/template_repo.rb +87 -0
  154. data/app/repositories/uffizzi_core/usage_repo.rb +9 -0
  155. data/app/repositories/uffizzi_core/user_repo.rb +11 -0
  156. data/app/responders/uffizzi_core/json_responder.rb +13 -0
  157. data/app/serializers/uffizzi_core/api/cli/v1/account/credential_serializer.rb +9 -0
  158. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer/compose_file_serializer.rb +7 -0
  159. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer/deployment_serializer.rb +13 -0
  160. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer.rb +30 -0
  161. data/app/serializers/uffizzi_core/api/cli/v1/projects/compose_file_serializer.rb +7 -0
  162. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/container_serializer.rb +32 -0
  163. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/user_serializer.rb +11 -0
  164. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer.rb +74 -0
  165. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/activity_item_serializer.rb +24 -0
  166. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer/container_config_file_serializer/config_file_serializer.rb +6 -0
  167. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer/container_config_file_serializer.rb +7 -0
  168. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer.rb +30 -0
  169. data/app/serializers/uffizzi_core/api/cli/v1/projects/secret_serializer.rb +5 -0
  170. data/app/serializers/uffizzi_core/api/cli/v1/short_project_serializer.rb +7 -0
  171. data/app/serializers/uffizzi_core/api/cli/v1/user_serializer/account_serializer.rb +5 -0
  172. data/app/serializers/uffizzi_core/api/cli/v1/user_serializer.rb +7 -0
  173. data/app/serializers/uffizzi_core/base_serializer.rb +7 -0
  174. data/app/serializers/uffizzi_core/controller/apply_config_file/config_file_serializer.rb +5 -0
  175. data/app/serializers/uffizzi_core/controller/create_credential/credential_serializer.rb +19 -0
  176. data/app/serializers/uffizzi_core/controller/create_deployment/deployment_serializer.rb +5 -0
  177. data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer/container_config_file_serializer/config_file_serializer.rb +8 -0
  178. data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer/container_config_file_serializer.rb +7 -0
  179. data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer.rb +70 -0
  180. data/app/serializers/uffizzi_core/controller/deploy_containers/credential_serializer.rb +5 -0
  181. data/app/services/uffizzi_core/account_service.rb +21 -0
  182. data/app/services/uffizzi_core/activity_item_service.rb +88 -0
  183. data/app/services/uffizzi_core/amazon/credential_service.rb +31 -0
  184. data/app/services/uffizzi_core/amazon_service.rb +45 -0
  185. data/app/services/uffizzi_core/azure/credential_service.rb +18 -0
  186. data/app/services/uffizzi_core/compose_file/builders/config_files_builder_service.rb +31 -0
  187. data/app/services/uffizzi_core/compose_file/builders/container_builder_service.rb +222 -0
  188. data/app/services/uffizzi_core/compose_file/builders/docker_repo_builder_service.rb +25 -0
  189. data/app/services/uffizzi_core/compose_file/builders/template_builder_service.rb +45 -0
  190. data/app/services/uffizzi_core/compose_file/builders/variables_builder_service.rb +58 -0
  191. data/app/services/uffizzi_core/compose_file/config_files_service.rb +52 -0
  192. data/app/services/uffizzi_core/compose_file/config_option_service.rb +41 -0
  193. data/app/services/uffizzi_core/compose_file/configs_options_service.rb +26 -0
  194. data/app/services/uffizzi_core/compose_file/container_service.rb +68 -0
  195. data/app/services/uffizzi_core/compose_file/continuous_preview_options_service.rb +57 -0
  196. data/app/services/uffizzi_core/compose_file/dependencies_service.rb +56 -0
  197. data/app/services/uffizzi_core/compose_file/errors_service.rb +46 -0
  198. data/app/services/uffizzi_core/compose_file/github_dependencies_service.rb +38 -0
  199. data/app/services/uffizzi_core/compose_file/ingress_options_service.rb +51 -0
  200. data/app/services/uffizzi_core/compose_file/parsers/services/healthcheck_parser_service.rb +73 -0
  201. data/app/services/uffizzi_core/compose_file/secrets_options_service.rb +28 -0
  202. data/app/services/uffizzi_core/compose_file/services_options/command_service.rb +18 -0
  203. data/app/services/uffizzi_core/compose_file/services_options/configs_service.rb +51 -0
  204. data/app/services/uffizzi_core/compose_file/services_options/deploy_service.rb +44 -0
  205. data/app/services/uffizzi_core/compose_file/services_options/entrypoint_service.rb +18 -0
  206. data/app/services/uffizzi_core/compose_file/services_options/env_file_service.rb +34 -0
  207. data/app/services/uffizzi_core/compose_file/services_options/environment_service.rb +20 -0
  208. data/app/services/uffizzi_core/compose_file/services_options/image_service.rb +89 -0
  209. data/app/services/uffizzi_core/compose_file/services_options/secrets_service.rb +35 -0
  210. data/app/services/uffizzi_core/compose_file/services_options_service.rb +57 -0
  211. data/app/services/uffizzi_core/compose_file/template_service.rb +55 -0
  212. data/app/services/uffizzi_core/compose_file/variables_service.rb +25 -0
  213. data/app/services/uffizzi_core/compose_file_service.rb +181 -0
  214. data/app/services/uffizzi_core/container_service.rb +42 -0
  215. data/app/services/uffizzi_core/controller_service.rb +86 -0
  216. data/app/services/uffizzi_core/credential_service.rb +44 -0
  217. data/app/services/uffizzi_core/deployment_service.rb +307 -0
  218. data/app/services/uffizzi_core/docker_hub/credential_service.rb +15 -0
  219. data/app/services/uffizzi_core/docker_hub_service.rb +77 -0
  220. data/app/services/uffizzi_core/github_container_registry/credential_service.rb +24 -0
  221. data/app/services/uffizzi_core/google/credential_service.rb +18 -0
  222. data/app/services/uffizzi_core/logs_service.rb +33 -0
  223. data/app/services/uffizzi_core/manage_activity_items_service.rb +159 -0
  224. data/app/services/uffizzi_core/project_service.rb +48 -0
  225. data/app/services/uffizzi_core/repo_service.rb +43 -0
  226. data/app/services/uffizzi_core/response_service.rb +13 -0
  227. data/app/services/uffizzi_core/starter_template_service.rb +200 -0
  228. data/app/services/uffizzi_core/template_service.rb +21 -0
  229. data/app/services/uffizzi_core/token_service.rb +19 -0
  230. data/app/services/uffizzi_core/user_access_service.rb +14 -0
  231. data/app/services/uffizzi_core/user_generator_service.rb +84 -0
  232. data/app/utils/uffizzi_core/converters.rb +33 -0
  233. data/app/validators/uffizzi_core/email_validator.rb +9 -0
  234. data/app/validators/uffizzi_core/environment_variable_list_validator.rb +15 -0
  235. data/app/validators/uffizzi_core/image_command_args_validator.rb +21 -0
  236. data/config/initializers/rswag_api.rb +15 -0
  237. data/config/initializers/rswag_ui.rb +15 -0
  238. data/config/initializers/swagger_yard.rb +17 -0
  239. data/config/locales/en.activerecord.yml +23 -0
  240. data/config/locales/en.yml +66 -0
  241. data/config/routes.rb +69 -0
  242. data/db/migrate/20220218121438_create_uffizzi_core_tables.rb +375 -0
  243. data/db/migrate/20220309110201_remove_secrets_from_projects.rb +7 -0
  244. data/db/migrate/20220310110150_create_project_secrets.rb +14 -0
  245. data/db/migrate/20220325113342_add_name_to_uffizzi_containers.rb +7 -0
  246. data/db/migrate/20220329123323_rename_project_secrets_to_secrets.rb +7 -0
  247. data/db/migrate/20220329124542_add_resource_to_secrets.rb +7 -0
  248. data/db/migrate/20220329143241_remove_project_ref_from_secrets.rb +7 -0
  249. data/db/migrate/20220419074956_add_health_check_to_containers.rb +7 -0
  250. data/db/migrate/20220525113412_rename_name_to_uffizzi_containers.rb +7 -0
  251. data/db/seeds.rb +16 -0
  252. data/lib/tasks/uffizzi_core_tasks.rake +19 -0
  253. data/lib/uffizzi_core/engine.rb +15 -0
  254. data/lib/uffizzi_core/version.rb +5 -0
  255. data/lib/uffizzi_core.rb +61 -0
  256. data/swagger/v1/swagger.json +1659 -0
  257. metadata +966 -0
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::ConfigsService
4
+ class << self
5
+ def parse(configs, global_configs_data)
6
+ return [] if configs.nil?
7
+
8
+ configs.map do |config|
9
+ config_data = case config
10
+ when String
11
+ process_short_syntax(config, global_configs_data)
12
+ when Hash
13
+ process_long_syntax(config, global_configs_data)
14
+ else
15
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :configs)
16
+ end
17
+
18
+ config_data
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def process_short_syntax(config_name, global_configs_data)
25
+ global_config = find_global_config!(config_name, global_configs_data)
26
+
27
+ {
28
+ source: UffizziCore::ComposeFile::ConfigOptionService.prepare_file_path_value(global_config[:config_file]),
29
+ target: global_config[:config_file],
30
+ }
31
+ end
32
+
33
+ def process_long_syntax(config_data, global_configs_data)
34
+ global_config = find_global_config!(config_data['source'], global_configs_data)
35
+ target = config_data['target'].blank? ? global_config[:config_file] : config_data['target']
36
+
37
+ {
38
+ source: UffizziCore::ComposeFile::ConfigOptionService.prepare_file_path_value(global_config[:config_file]),
39
+ target: target,
40
+ }
41
+ end
42
+
43
+ def find_global_config!(config_name, global_configs_data)
44
+ detected_config = global_configs_data.detect { |global_config_data| global_config_data[:config_name] == config_name }
45
+
46
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.global_config_not_found', config: config_name) if detected_config.nil?
47
+
48
+ detected_config
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::DeployService
4
+ class << self
5
+ def parse(deploy_data)
6
+ return {} if deploy_data.blank?
7
+
8
+ auto = prepare_deploy_auto(deploy_data)
9
+ memory = prepare_memory(deploy_data)
10
+
11
+ {
12
+ auto: auto,
13
+ memory: memory,
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ def prepare_deploy_auto(deploy_data)
20
+ auto = deploy_data['x-uffizzi-auto-deploy-updates']
21
+ return auto if auto.nil? || auto.in?([true, false])
22
+
23
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_bool_value', field: :auto, value: auto)
24
+ end
25
+
26
+ def prepare_memory(deploy_data)
27
+ memory = deploy_data.dig('resources', 'limits', 'memory')
28
+ return nil if memory.blank?
29
+
30
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_memory_type', value: memory) unless memory.is_a?(String)
31
+
32
+ value, postfix = memory.scan(/^([0-9]+)([a-zA-Z]+)$/).flatten
33
+
34
+ if postfix.nil? || !Settings.compose.memory_postfixes.include?(postfix.downcase)
35
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_memory_postfix', value: memory)
36
+ end
37
+
38
+ {
39
+ value: value.to_i,
40
+ postfix: postfix.downcase,
41
+ }
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::EntrypointService
4
+ class << self
5
+ def parse(entrypoint_data)
6
+ return nil if entrypoint_data.blank?
7
+
8
+ case entrypoint_data
9
+ when String
10
+ [entrypoint_data]
11
+ when Array
12
+ entrypoint_data
13
+ else
14
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :entrypoint)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::EnvFileService
4
+ class << self
5
+ def parse(env_file)
6
+ env_files = case env_file
7
+ when String
8
+ [prepare_file_path(env_file)]
9
+ when Array
10
+ env_file.map { |env_file_path| prepare_file_path(env_file_path) }
11
+ else
12
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :env_file)
13
+ end
14
+
15
+ check_duplicates(env_files)
16
+
17
+ env_files
18
+ end
19
+
20
+ private
21
+
22
+ def prepare_file_path(env_file_path)
23
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.empty_env_file') if env_file_path.blank?
24
+
25
+ UffizziCore::ComposeFile::ConfigOptionService.prepare_file_path_value(env_file_path)
26
+ end
27
+
28
+ def check_duplicates(env_files)
29
+ return if env_files.uniq.length == env_files.length
30
+
31
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.env_file_duplicates', values: env_files)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::EnvironmentService
4
+ extend UffizziCore::ComposeFile::VariablesService
5
+
6
+ class << self
7
+ def parse(environment)
8
+ return [] if environment.blank?
9
+
10
+ case environment
11
+ when Array
12
+ environment.map { |variable| parse_variable_from_string(variable) }
13
+ when Hash
14
+ environment.to_a.map { |variable| parse_variable_from_array(variable) }
15
+ else
16
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :environment)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::ImageService
4
+ class << self
5
+ def parse(image)
6
+ return {} if image.blank?
7
+
8
+ image_parts = image.downcase.split(':')
9
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_image_value', value: image) if image_parts.count > 2
10
+
11
+ image_name, tag = image_parts
12
+ tag = Settings.compose.default_tag if tag.blank?
13
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_image_value', value: image) if image_name.blank?
14
+
15
+ if valid_image_url?(image_name)
16
+ url, namespace, name = parse_image_url(image_name)
17
+ else
18
+ namespace, name = parse_docker_hub_image(image_name)
19
+ end
20
+
21
+ {
22
+ registry_url: url,
23
+ namespace: namespace,
24
+ name: name,
25
+ tag: tag,
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def valid_image_url?(image_name)
32
+ url = prepare_image_url(image_name)
33
+
34
+ URI(url).host.present? && URI(url).host =~ /\w+\.\w+/ && URI(url).path.present?
35
+ end
36
+
37
+ def prepare_image_url(image_name)
38
+ if image_name.start_with?('https://')
39
+ image_name
40
+ else
41
+ "https://#{image_name}"
42
+ end
43
+ end
44
+
45
+ def parse_image_url(image_name)
46
+ prepared_url = prepare_image_url(image_name)
47
+
48
+ uri = URI(prepared_url)
49
+ url = uri.host
50
+ path = uri.path.delete_suffix('/').delete_prefix('/')
51
+
52
+ namespace, name = parse_image_path(path)
53
+
54
+ [url, namespace, name]
55
+ end
56
+
57
+ def parse_image_path(path)
58
+ path_parts = path.split('/', 2)
59
+
60
+ if path_parts.count == 1
61
+ namespace = nil
62
+ name = path_parts.first
63
+ else
64
+ namespace = path_parts.first
65
+ name = path_parts.last
66
+ end
67
+
68
+ [namespace, name]
69
+ end
70
+
71
+ def parse_docker_hub_image(image_name)
72
+ if contains_account_name?(image_name)
73
+ namespace = image_name.split('/').first
74
+ name = image_name.split('/', 2).last.delete_suffix('/')
75
+ else
76
+ namespace = Settings.docker_hub.public_namespace
77
+ name = image_name
78
+ end
79
+
80
+ [namespace, name]
81
+ end
82
+
83
+ def contains_account_name?(image_name)
84
+ image_name_parts = image_name.split('/')
85
+
86
+ image_name_parts.count > 1
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::SecretsService
4
+ class << self
5
+ def parse(secrets, global_secrets_data)
6
+ return [] if secrets.nil?
7
+
8
+ secrets.map do |secret|
9
+ variable_name = if secret.is_a?(String)
10
+ process_short_syntax(secret, global_secrets_data)
11
+ else
12
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :secrets)
13
+ end
14
+
15
+ variable_name
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def process_short_syntax(secret_name, global_secrets_data)
22
+ global_secret = find_global_secret!(secret_name, global_secrets_data)
23
+
24
+ global_secret[:secret_variable]
25
+ end
26
+
27
+ def find_global_secret!(secret_name, global_secrets_data)
28
+ detected_secret = global_secrets_data.detect { |global_secret_data| global_secret_data[:secret_name] == secret_name }
29
+ error_message = I18n.t('compose.global_secret_not_found', secret: secret_name)
30
+ raise UffizziCore::ComposeFile::ParseError, error_message if detected_secret.nil?
31
+
32
+ detected_secret
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptionsService
4
+ class << self
5
+ def parse(services, global_configs_data, global_secrets_data, compose_payload)
6
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.no_services') if services.nil? || services.keys.empty?
7
+
8
+ services.keys.map do |service|
9
+ service_data = prepare_service_data(service, services.fetch(service), global_configs_data, global_secrets_data, compose_payload)
10
+
11
+ if service_data[:image].blank? && service_data[:build].blank?
12
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.image_build_no_specified', value: service)
13
+ end
14
+
15
+ service_data
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def prepare_service_data(service_name, service_data, global_configs_data, global_secrets_data, _compose_payload)
22
+ options_data = {}
23
+ service_data.each_pair do |key, value|
24
+ service_key = key.to_sym
25
+
26
+ options_data[service_key] = case service_key
27
+ when :image
28
+ UffizziCore::ComposeFile::ServicesOptions::ImageService.parse(value)
29
+ when :build
30
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.not_implemented', option: :build)
31
+ when :env_file
32
+ UffizziCore::ComposeFile::ServicesOptions::EnvFileService.parse(value)
33
+ when :environment
34
+ UffizziCore::ComposeFile::ServicesOptions::EnvironmentService.parse(value)
35
+ when :configs
36
+ UffizziCore::ComposeFile::ServicesOptions::ConfigsService.parse(value, global_configs_data)
37
+ when :secrets
38
+ UffizziCore::ComposeFile::ServicesOptions::SecretsService.parse(value, global_secrets_data)
39
+ when :deploy
40
+ UffizziCore::ComposeFile::ServicesOptions::DeployService.parse(value)
41
+ when :entrypoint
42
+ UffizziCore::ComposeFile::ServicesOptions::EntrypointService.parse(value)
43
+ when :command
44
+ UffizziCore::ComposeFile::ServicesOptions::CommandService.parse(value)
45
+ when :healthcheck
46
+ UffizziCore::ComposeFile::Parsers::Services::HealthcheckParserService.parse(value)
47
+ when :'x-uffizzi-continuous-preview', :'x-uffizzi-continuous-previews'
48
+ UffizziCore::ComposeFile::ContinuousPreviewOptionsService.parse(value)
49
+ end
50
+ end
51
+
52
+ options_data[:container_name] = service_name
53
+
54
+ options_data
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::TemplateService
4
+ def initialize(cli_form, project, user)
5
+ @project = project
6
+ @user = user
7
+ @compose_dependencies = cli_form.compose_dependencies
8
+ @compose_data = cli_form.compose_data
9
+ @compose_repositories = cli_form.compose_repositories
10
+ end
11
+
12
+ def create_template(compose_file_form)
13
+ compose_file_template_form = build_compose_file_template_form(compose_file_form)
14
+ return compose_file_template_form.errors if compose_file_template_form.invalid?
15
+
16
+ template_form = build_template_form(compose_file_form, compose_file_template_form)
17
+ return template_form.errors if template_form.invalid?
18
+
19
+ template_form.save
20
+
21
+ nil
22
+ end
23
+
24
+ private
25
+
26
+ def build_compose_file_template_form(compose_file_form)
27
+ credentials = @project.account.credentials
28
+ compose_file_template_form = UffizziCore::Api::Cli::V1::ComposeFile::TemplateForm.new
29
+ compose_file_template_form.compose_data = @compose_data
30
+ compose_file_template_form.source = compose_file_form.source
31
+ compose_file_template_form.credentials = credentials
32
+ compose_file_template_form.project = @project
33
+ compose_file_template_form.user = @user
34
+ compose_file_template_form.compose_dependencies = @compose_dependencies
35
+ compose_file_template_form.compose_repositories = @compose_repositories
36
+ compose_file_template_form.assign_template_attributes!
37
+
38
+ compose_file_template_form
39
+ end
40
+
41
+ def build_template_form(compose_file_form, compose_file_template_form)
42
+ attributes = compose_file_template_form.template_attributes
43
+ source = compose_file_template_form.source
44
+ template = compose_file_form.template
45
+ template = @project.templates.find_or_initialize_by(name: source) if !template.present?
46
+ template.assign_attributes(attributes)
47
+ template_form = template.becomes(UffizziCore::Api::Cli::V1::Template::CreateForm)
48
+ template_form.project = @project
49
+ template_form.added_by = @user
50
+ template_form.compose_file = compose_file_form
51
+ template_form.creation_source = UffizziCore::Template.creation_source.compose_file
52
+
53
+ template_form
54
+ end
55
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UffizziCore::ComposeFile::VariablesService
4
+ def parse_variable_from_string(str)
5
+ variable_parts = str.split('=', 2)
6
+
7
+ parse_variable_from_array(variable_parts)
8
+ end
9
+
10
+ def parse_variable_from_array(arr)
11
+ name = arr.first.to_s
12
+ value = arr.last.to_s
13
+
14
+ build_variable(name, value)
15
+ end
16
+
17
+ def build_variable(name, value)
18
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.no_variable_name', name: name, value: value) if name.blank?
19
+
20
+ {
21
+ name: name,
22
+ value: value,
23
+ }
24
+ end
25
+ end
@@ -0,0 +1,181 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UffizziCore::ComposeFileService
4
+ class << self
5
+ def create(params, kind)
6
+ compose_file_form = create_compose_form(params, kind)
7
+
8
+ process_compose_file(compose_file_form, params)
9
+ end
10
+
11
+ def update(compose_file, params)
12
+ compose_file_form = create_update_compose_form(compose_file, params)
13
+
14
+ process_compose_file(compose_file_form, params)
15
+ end
16
+
17
+ def parse(compose_content, compose_payload = {})
18
+ compose_data = load_compose_data(compose_content)
19
+ check_config_options_format(compose_data)
20
+ configs_data = UffizziCore::ComposeFile::ConfigsOptionsService.parse(compose_data['configs'])
21
+ secrets_data = UffizziCore::ComposeFile::SecretsOptionsService.parse(compose_data['secrets'])
22
+ containers_data = UffizziCore::ComposeFile::ServicesOptionsService.parse(compose_data['services'], configs_data, secrets_data,
23
+ compose_payload)
24
+
25
+ continuous_preview_option = UffizziCore::ComposeFile::ConfigOptionService.continuous_preview_option(compose_data)
26
+ continuous_preview_data = UffizziCore::ComposeFile::ContinuousPreviewOptionsService.parse(continuous_preview_option)
27
+
28
+ ingress_option = UffizziCore::ComposeFile::ConfigOptionService.ingress_option(compose_data)
29
+ ingress_data = UffizziCore::ComposeFile::IngressOptionsService.parse(ingress_option, compose_data['services'])
30
+
31
+ {
32
+ containers: containers_data,
33
+ ingress: ingress_data,
34
+ continuous_preview: continuous_preview_data,
35
+ }
36
+ end
37
+
38
+ def build_template_attributes(compose_data, source, credentials, project, compose_dependencies = [], compose_repositories = [])
39
+ builder = UffizziCore::ComposeFile::Builders::TemplateBuilderService.new(credentials, project, compose_repositories)
40
+
41
+ builder.build_attributes(compose_data, compose_dependencies, source)
42
+ end
43
+
44
+ def containers_credentials(compose_data, credentials)
45
+ containers = compose_data[:containers]
46
+ detected_credentials = containers.map do |container|
47
+ UffizziCore::ComposeFile::ContainerService.credential_for_container(container, credentials)
48
+ end
49
+
50
+ result = []
51
+ detected_credentials.compact
52
+ .group_by { |credential| credential[:id] }
53
+ .each_pair { |_id, value| result << value.first }
54
+ result
55
+ end
56
+
57
+ def has_secret?(compose_file, secret)
58
+ containers = compose_file.template.payload['containers_attributes']
59
+
60
+ containers.any? { |container| UffizziCore::ComposeFile::ContainerService.has_secret?(container, secret) }
61
+ end
62
+
63
+ def update_secret!(compose_file, secret)
64
+ compose_file.template.payload['containers_attributes'].map do |container|
65
+ if UffizziCore::ComposeFile::ContainerService.has_secret?(container, secret)
66
+ container = UffizziCore::ComposeFile::ContainerService.update_secret(container, secret)
67
+ end
68
+
69
+ container
70
+ end
71
+
72
+ compose_file.template.save!
73
+
74
+ compose_file
75
+ end
76
+
77
+ def secrets_valid?(compose_file, secrets)
78
+ secret_names = secrets.pluck('name')
79
+
80
+ compose_file.template.payload['containers_attributes'].all? do |container|
81
+ container['secret_variables'].all? { |secret| secret_names.include?(secret['name']) }
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def process_compose_file(compose_file_form, params)
88
+ cli_form = UffizziCore::Api::Cli::V1::ComposeFile::CliForm.new
89
+ cli_form.content = compose_file_form.content
90
+ return [compose_file_form, cli_form.errors] if cli_form.invalid?
91
+
92
+ dependencies = params[:dependencies].to_a
93
+ compose_data = cli_form.compose_data
94
+ compose_dependencies = build_compose_dependecies(compose_data, compose_file_form.path, dependencies)
95
+ cli_form.compose_dependencies = compose_dependencies
96
+
97
+ persist!(compose_file_form, cli_form)
98
+ end
99
+
100
+ def create_compose_form(params, kind)
101
+ compose_file_params = params[:compose_file_params]
102
+ compose_file_form = UffizziCore::Api::Cli::V1::ComposeFile::CreateForm.new(compose_file_params)
103
+ compose_file_form.project = params[:project]
104
+ compose_file_form.added_by = params[:user]
105
+ compose_file_form.content = compose_file_params[:content]
106
+ compose_file_form.kind = kind
107
+ payload_dependencies = prepare_compose_file_dependencies(params[:dependencies])
108
+ compose_file_form.payload['dependencies'] = payload_dependencies
109
+
110
+ compose_file_form
111
+ end
112
+
113
+ def create_update_compose_form(compose_file, params)
114
+ compose_file_form = compose_file.becomes(UffizziCore::Api::Cli::V1::ComposeFile::UpdateForm)
115
+ compose_file_params = params[:compose_file_params]
116
+ compose_file_form.assign_attributes(compose_file_params)
117
+ payload_dependencies = prepare_compose_file_dependencies(params[:dependencies])
118
+ compose_file_form.payload['dependencies'] = payload_dependencies
119
+
120
+ compose_file_form
121
+ end
122
+
123
+ def build_compose_dependecies(compose_data, compose_path, dependencies)
124
+ return [] if dependencies.empty?
125
+
126
+ UffizziCore::ComposeFile::DependenciesService.build_dependencies(compose_data, compose_path, dependencies)
127
+ end
128
+
129
+ def prepare_compose_file_dependencies(compose_dependencies)
130
+ compose_dependencies.map do |dependency|
131
+ {
132
+ path: dependency[:path],
133
+ }
134
+ end
135
+ end
136
+
137
+ def persist!(compose_file_form, cli_form)
138
+ errors = []
139
+ ActiveRecord::Base.transaction do
140
+ if !compose_file_form.save
141
+ errors = compose_file_form.errors
142
+ raise ActiveRecord::Rollback
143
+ end
144
+
145
+ config_files_service = UffizziCore::ComposeFile::ConfigFilesService.new(compose_file_form)
146
+ errors = config_files_service.create_config_files(cli_form.compose_dependencies)
147
+ raise ActiveRecord::Rollback if errors.present?
148
+
149
+ project = compose_file_form.project
150
+ user = compose_file_form.added_by
151
+ template_service = UffizziCore::ComposeFile::TemplateService.new(cli_form, project, user)
152
+ errors = template_service.create_template(compose_file_form)
153
+
154
+ raise ActiveRecord::Rollback if errors.present?
155
+ end
156
+ [compose_file_form, errors]
157
+ end
158
+
159
+ def load_compose_data(compose_content)
160
+ begin
161
+ compose_data = YAML.safe_load(compose_content, aliases: true)
162
+ rescue Psych::SyntaxError
163
+ raise UffizziCore::ComposeFile::ParseError, 'Invalid compose file'
164
+ end
165
+
166
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.unsupported_file') if compose_data.nil?
167
+
168
+ compose_data
169
+ end
170
+
171
+ def check_config_options_format(compose_data)
172
+ options = UffizziCore::ComposeFile::ConfigOptionService.config_options(compose_data)
173
+
174
+ options.each do |option|
175
+ next if UffizziCore::ComposeFile::ConfigOptionService.valid_option_format?(option)
176
+
177
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_config_option', value: option)
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ContainerService
4
+ class << self
5
+ def pod_name(container)
6
+ return container.controller_name if container.controller_name.present?
7
+
8
+ formatted_name = container.image_name
9
+ formatted_name.parameterize.gsub('_', '-')
10
+ end
11
+
12
+ def target_port_value(container)
13
+ container.port
14
+ end
15
+
16
+ def defines_env?(container, name)
17
+ return false if container.variables.nil?
18
+
19
+ container.variables.select { |v| v['name'].downcase.to_sym == name.to_sym }.any?
20
+ end
21
+
22
+ def continuously_deploy_enabled?(container)
23
+ container.aasm(:continuously_deploy).current_state == UffizziCore::Container::STATE_ENABLED
24
+ end
25
+
26
+ def valid_memory_limit?(container)
27
+ max_memory_limit = container.deployment.project.account.container_memory_limit
28
+ container_memory_limit = container.memory_limit
29
+ return true if max_memory_limit.nil? || container_memory_limit.nil?
30
+
31
+ container_memory_limit <= max_memory_limit
32
+ end
33
+
34
+ def valid_memory_request?(container)
35
+ max_memory_limit = container.deployment.project.account.container_memory_limit
36
+ container_memory_request = container.memory_request
37
+ return true if max_memory_limit.nil? || container_memory_request.nil?
38
+
39
+ container_memory_request <= max_memory_limit
40
+ end
41
+ end
42
+ end