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,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Builders::TemplateBuilderService
4
+ attr_accessor :credentials, :project, :repositories
5
+
6
+ def initialize(credentials, project, repositories = [])
7
+ @credentials = credentials
8
+ @project = project
9
+ @repositories = repositories
10
+ end
11
+
12
+ def build_attributes(compose_data, compose_dependencies, source)
13
+ containers_data = compose_data[:containers]
14
+ ingress_data = compose_data[:ingress]
15
+ continuous_preview_global_data = compose_data[:continuous_preview]
16
+
17
+ containers_attributes = build_containers_attributes(
18
+ containers_data,
19
+ ingress_data,
20
+ continuous_preview_global_data,
21
+ compose_dependencies,
22
+ )
23
+
24
+ {
25
+ name: source,
26
+ payload: {
27
+ containers_attributes: containers_attributes,
28
+ },
29
+ }
30
+ end
31
+
32
+ private
33
+
34
+ def build_containers_attributes(containers_data, ingress_data, continuous_preview_global_data, compose_dependencies)
35
+ containers_data.map do |container_data|
36
+ container_attributes(container_data, ingress_data, continuous_preview_global_data, compose_dependencies)
37
+ end
38
+ end
39
+
40
+ def container_attributes(containers_data, ingress_data, continuous_preview_global_data, compose_dependencies)
41
+ builder = UffizziCore::ComposeFile::Builders::ContainerBuilderService.new(credentials, project, repositories)
42
+
43
+ builder.build_attributes(containers_data, ingress_data, continuous_preview_global_data, compose_dependencies)
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Builders::VariablesBuilderService
4
+ attr_accessor :project
5
+
6
+ require 'dotenv'
7
+
8
+ def initialize(project)
9
+ @project = project
10
+ end
11
+
12
+ def build_attributes(variables_data, dependencies)
13
+ variables = variables_data
14
+ variables_from_dependencies = variables_from_dependencies(dependencies)
15
+
16
+ variables + variables_from_dependencies
17
+ end
18
+
19
+ def build_secret_attributes(secrets)
20
+ project_secrets = project.secrets || []
21
+
22
+ secrets.uniq.map do |secret|
23
+ detected_secret = project_secrets.detect { |project_secret| project_secret['name'] == secret }
24
+ error_message = I18n.t('compose.project_secret_not_found', secret: secret)
25
+ raise UffizziCore::ComposeFile::SecretsError, error_message if detected_secret.nil?
26
+
27
+ build_variable(detected_secret['name'], detected_secret['value'])
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def variables_from_dependencies(dependencies)
34
+ variables = dependencies.map do |dependency|
35
+ variables_data = parse_variables_from_dependency(dependency)
36
+
37
+ variables_data.map { |variable_data| build_variable(variable_data.first, variable_data.last) }
38
+ end
39
+
40
+ variables.flatten
41
+ end
42
+
43
+ def parse_variables_from_dependency(dependency)
44
+ content = dependency[:content]
45
+ return [] if content.blank?
46
+
47
+ variables_content = UffizziCore::ComposeFile::GithubDependenciesService.content(dependency)
48
+ parser = Dotenv::Parser.new(variables_content)
49
+ parser.call.to_a
50
+ end
51
+
52
+ def build_variable(name, value)
53
+ {
54
+ name: name,
55
+ value: value,
56
+ }
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ConfigFilesService
4
+ def initialize(compose_file_form)
5
+ @compose_file_form = compose_file_form
6
+ @repository_id = compose_file_form.repository_id
7
+ @branch = compose_file_form.branch
8
+ @path = compose_file_form.path
9
+ @user = compose_file_form.added_by
10
+ @project = compose_file_form.project
11
+ end
12
+
13
+ def create_config_files(compose_dependencies)
14
+ configs_dependencies = UffizziCore::ComposeFile::GithubDependenciesService.configs_dependencies(compose_dependencies)
15
+ errors = []
16
+ configs_dependencies.each do |config_dependency|
17
+ errors = create_config_file(config_dependency)
18
+ errors << errors if errors
19
+ end
20
+
21
+ errors
22
+ end
23
+
24
+ private
25
+
26
+ def create_config_file(config_dependency)
27
+ source = UffizziCore::ComposeFile::GithubDependenciesService.build_source_path(@path, config_dependency[:path], @repository_id, @branch)
28
+ config_file = @project.config_files.find_or_initialize_by(source: source)
29
+ attributes = {
30
+ filename: UffizziCore::ComposeFile::GithubDependenciesService.filename(config_dependency),
31
+ payload: UffizziCore::ComposeFile::GithubDependenciesService.content(config_dependency),
32
+ }
33
+
34
+ config_file.assign_attributes(attributes)
35
+ config_file_form = build_config_file_form(config_file)
36
+ return config_file_form.errors if config_file_form.invalid?
37
+
38
+ config_file_form.save
39
+
40
+ nil
41
+ end
42
+
43
+ def build_config_file_form(config_file)
44
+ config_file_form = config_file.becomes(UffizziCore::Api::Cli::V1::ConfigFile::CreateForm)
45
+ config_file_form.project = @project
46
+ config_file_form.added_by = @user
47
+ config_file_form.compose_file = @compose_file_form
48
+ config_file_form.creation_source = UffizziCore::ConfigFile.creation_source.compose_file
49
+
50
+ config_file_form
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ConfigOptionService
4
+ class << self
5
+ def valid_option_format?(option)
6
+ if option.is_a?(TrueClass) || option.is_a?(FalseClass)
7
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.boolean_option', value: option)
8
+ end
9
+
10
+ option.match(/^[a-zA-Z_][a-zA-Z0-9._\-]+$/).present?
11
+ end
12
+
13
+ def config_options(compose_data)
14
+ compose_data.each_with_object([]) do |(key, value), keys|
15
+ if compose_data.equal?(value)
16
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.infinite_recursion', key: key)
17
+ end
18
+
19
+ keys << key
20
+ keys.concat(config_options(value)) if value.is_a?(Hash)
21
+ end
22
+ end
23
+
24
+ def prepare_file_path_value(file_path)
25
+ pathname = Pathname.new(file_path)
26
+
27
+ pathname.cleanpath.to_s.strip.delete_prefix('/')
28
+ end
29
+
30
+ def ingress_option(compose_data)
31
+ compose_data.dig('x-uffizzi', 'ingress').presence || compose_data['x-uffizzi-ingress'].presence
32
+ end
33
+
34
+ def continuous_preview_option(compose_data)
35
+ compose_data.dig('x-uffizzi', 'continuous_preview').presence ||
36
+ compose_data.dig('x-uffizzi', 'continuous_previews').presence ||
37
+ compose_data['x-uffizzi-continuous-preview'].presence ||
38
+ compose_data['x-uffizzi-continuous-previews'].presence
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ConfigsOptionsService
4
+ class << self
5
+ def parse(configs_data)
6
+ return [] if configs_data.nil?
7
+
8
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :configs) unless configs_data.is_a?(Hash)
9
+
10
+ configs = []
11
+ configs_data.each_pair do |config_name, config_data|
12
+ if config_data['file'].blank?
13
+ raise UffizziCore::ComposeFile::ParseError,
14
+ I18n.t('compose.config_file_option_empty', config_name: config_name)
15
+ end
16
+
17
+ configs << {
18
+ config_name: config_name,
19
+ config_file: config_data['file'],
20
+ }
21
+ end
22
+
23
+ configs
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ContainerService
4
+ class << self
5
+ def azure?(container)
6
+ registry_url = container.dig(:image, :registry_url)
7
+
8
+ registry_url.present? && registry_url.include?('azurecr.io')
9
+ end
10
+
11
+ def google?(container)
12
+ registry_url = container.dig(:image, :registry_url)
13
+
14
+ registry_url.present? && registry_url.include?('gcr.io')
15
+ end
16
+
17
+ def amazon?(container)
18
+ registry_url = container.dig(:image, :registry_url)
19
+
20
+ registry_url.present? && registry_url.include?('amazonaws.com')
21
+ end
22
+
23
+ def docker_hub?(container)
24
+ registry_url = container.dig(:image, :registry_url)
25
+ repository_url = container.dig(:build, :repository_url)
26
+
27
+ registry_url.nil? && repository_url.nil?
28
+ end
29
+
30
+ def github_container_registry?(container)
31
+ registry_url = container.dig(:image, :registry_url)
32
+
33
+ registry_url.present? && registry_url.include?('ghcr.io')
34
+ end
35
+
36
+ def has_secret?(container, secret)
37
+ container['secret_variables'].any? { |container_secret| container_secret['name'] == secret['name'] }
38
+ end
39
+
40
+ def update_secret(container, secret)
41
+ secret_index = container['secret_variables'].find_index { |container_secret| container_secret['name'] == secret['name'] }
42
+ container['secret_variables'][secret_index] = secret
43
+
44
+ container
45
+ end
46
+
47
+ def credential_for_container(container, credentials)
48
+ if UffizziCore::ComposeFile::ContainerService.azure?(container)
49
+ detect_credential(credentials, :azure)
50
+ elsif UffizziCore::ComposeFile::ContainerService.docker_hub?(container)
51
+ detect_credential(credentials, :docker_hub)
52
+ elsif UffizziCore::ComposeFile::ContainerService.google?(container)
53
+ detect_credential(credentials, :google)
54
+ end
55
+ end
56
+
57
+ def detect_credential(credentials, type)
58
+ credential = credentials.detect do |item|
59
+ item.send("#{type}?")
60
+ end
61
+
62
+ error_message = "Invalid credential: #{type}"
63
+ raise UffizziCore::ComposeFile::CredentialError.new(error_message) if credential.nil?
64
+
65
+ credential
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ContinuousPreviewOptionsService
4
+ class << self
5
+ def parse(continuous_preview_data)
6
+ return {} if continuous_preview_data.nil?
7
+
8
+ {
9
+ deploy_preview_when_pull_request_is_opened: trigger_value(continuous_preview_data, 'deploy_preview_when_pull_request_is_opened'),
10
+ delete_preview_when_pull_request_is_closed: trigger_value(continuous_preview_data, 'delete_preview_when_pull_request_is_closed'),
11
+ deploy_preview_when_image_tag_is_created: trigger_value(continuous_preview_data, 'deploy_preview_when_image_tag_is_created'),
12
+ delete_preview_when_image_tag_is_updated: trigger_value(continuous_preview_data, 'delete_preview_when_image_tag_is_updated'),
13
+ delete_preview_after: delete_preview_after_value(continuous_preview_data['delete_preview_after']),
14
+ share_to_github: trigger_value(continuous_preview_data, 'share_to_github'),
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ def trigger_value(continuous_preview_data, field)
21
+ value = continuous_preview_data[field]
22
+ return nil if value.nil?
23
+ return value if value.in?([true, false])
24
+
25
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_bool_value', field: field, value: value)
26
+ end
27
+
28
+ def delete_preview_after_value(value)
29
+ return {} if value.blank?
30
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_string', option: :delete_preview_after) unless value.is_a?(String)
31
+
32
+ hours, postfix = value.scan(/^([0-9]+)([a-zA-Z])$/).flatten
33
+
34
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_integer', option: :delete_preview_after) if hours.nil?
35
+
36
+ formatted_hours = hours.to_i
37
+ if formatted_hours < Settings.compose.delete_after_min_value
38
+ raise UffizziCore::ComposeFile::ParseError,
39
+ I18n.t('compose.invalid_delete_after_min', value: Settings.compose.delete_after_min_value)
40
+ end
41
+
42
+ if formatted_hours > Settings.compose.delete_after_max_value
43
+ raise UffizziCore::ComposeFile::ParseError,
44
+ I18n.t('compose.invalid_delete_after_max', value: Settings.compose.delete_after_max_value)
45
+ end
46
+
47
+ if postfix.nil? || !Settings.compose.delete_after_postfixes.include?(postfix.downcase)
48
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_delete_after_postfix')
49
+ end
50
+
51
+ {
52
+ value: formatted_hours,
53
+ postfix: postfix.downcase,
54
+ }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::DependenciesService
4
+ ENV_FILE_TYPE = 'env_file'
5
+ CONFIG_TYPE = 'config'
6
+
7
+ class << self
8
+ def build_dependencies(compose_data, compose_path, dependencies_params)
9
+ dependencies = compose_data[:containers].map do |container|
10
+ env_file_dependencies = build_env_files_dependencies(container, compose_path, dependencies_params)
11
+ configs_dependencies = build_configs_dependencies(container, compose_path, dependencies_params)
12
+
13
+ env_file_dependencies + configs_dependencies
14
+ end
15
+
16
+ dependencies.compact.flatten
17
+ end
18
+
19
+ def build_env_files_dependencies(container, compose_path, dependencies_params)
20
+ env_files = container[:env_file]
21
+ return [] unless env_files.present?
22
+
23
+ env_files.map do |path|
24
+ dependency = dependencies_params.detect { |item| item[:path] == path }
25
+ source = build_source_path(compose_path, path)
26
+
27
+ base_file_params(dependency, container).merge(source: source, type: ENV_FILE_TYPE)
28
+ end
29
+ end
30
+
31
+ def build_configs_dependencies(container, compose_path, dependencies_params)
32
+ configs = container[:configs]
33
+ return [] unless configs.present?
34
+
35
+ configs.map do |config|
36
+ dependency = dependencies_params.detect { |item| item[:path] == config[:source] }
37
+ source = build_source_path(compose_path, dependency[:path])
38
+
39
+ base_file_params(dependency, container).merge(source: source, type: CONFIG_TYPE)
40
+ end
41
+ end
42
+
43
+ def base_file_params(dependency, container)
44
+ {
45
+ content: dependency[:content],
46
+ path: dependency[:path],
47
+ container_name: container[:container_name],
48
+ }
49
+ end
50
+
51
+ def build_source_path(compose_path, dependency_path)
52
+ prepared_compose_path = Pathname.new(compose_path).basename.to_s
53
+ "#{prepared_compose_path}/#{dependency_path}"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ErrorsService
4
+ SECRETS_ERROR_KEY = 'secret_variables'
5
+ class << self
6
+ def has_error?(compose_file, error_code)
7
+ error = compose_file.payload.dig('errors', error_code)
8
+
9
+ error.present?
10
+ end
11
+
12
+ def has_errors?(compose_file)
13
+ compose_file.payload['errors'].present?
14
+ end
15
+
16
+ def update_compose_errors!(compose_file, errors, invalid_content)
17
+ compose_file.payload['errors'] = errors
18
+ compose_file.set_invalid if compose_file.valid_file?
19
+ compose_file.content = invalid_content
20
+
21
+ compose_file.save!
22
+
23
+ compose_file
24
+ end
25
+
26
+ def reset_compose_errors!(compose_file)
27
+ compose_file.payload['errors'] = nil
28
+ compose_file.set_valid
29
+
30
+ compose_file.save!
31
+
32
+ compose_file
33
+ end
34
+
35
+ def reset_error!(compose_file, error_code)
36
+ errors = compose_file.payload['errors']
37
+ return if errors.nil?
38
+
39
+ new_errors = errors.except(error_code)
40
+ compose_file.payload['errors'] = new_errors
41
+ compose_file.save!
42
+
43
+ compose_file
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::GithubDependenciesService
4
+ ENV_FILE_TYPE = 'env_file'
5
+ CONFIG_TYPE = 'config'
6
+
7
+ class << self
8
+ def filename(dependency)
9
+ pathname = Pathname.new(dependency[:path])
10
+
11
+ pathname.basename.to_s
12
+ end
13
+
14
+ def content(dependency)
15
+ Base64.decode64(dependency[:content])
16
+ end
17
+
18
+ def env_file_dependencies_for_container(dependencies, container_name)
19
+ dependencies.select { |dependency| dependency[:type] == ENV_FILE_TYPE && dependency[:container_name] == container_name }
20
+ end
21
+
22
+ def configs_dependencies_for_container(dependencies, container_name)
23
+ configs_dependencies(dependencies).select { |dependency| dependency[:container_name] == container_name }
24
+ end
25
+
26
+ def configs_dependencies(dependencies)
27
+ dependencies.select { |dependency| dependency[:type] == CONFIG_TYPE }
28
+ end
29
+
30
+ def build_source_path(compose_path, dependency_path, repository_id, branch)
31
+ prepared_compose_path = Pathname.new(compose_path).basename.to_s
32
+ base_source = "#{prepared_compose_path}/#{dependency_path}"
33
+ return base_source if repository_id.blank?
34
+
35
+ "#{repository_id}/#{branch}/#{base_source}"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::IngressOptionsService
4
+ class << self
5
+ def parse(ingress_data, services_data)
6
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.no_ingress') if ingress_data.nil?
7
+
8
+ container_name = container_name(ingress_data, services_data)
9
+ port = port(ingress_data)
10
+
11
+ {
12
+ container_name: container_name,
13
+ port: port,
14
+ }
15
+ end
16
+
17
+ private
18
+
19
+ def container_name(ingress_data, services_data)
20
+ container_name = ingress_data['service']
21
+
22
+ if container_name.nil?
23
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.ingress_service_not_found')
24
+ end
25
+
26
+ unless services_data.keys.include?(container_name)
27
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_ingress_service', value: container_name)
28
+ end
29
+
30
+ container_name
31
+ end
32
+
33
+ def port(ingress_data)
34
+ port = ingress_data['port']
35
+
36
+ if port.nil?
37
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.ingress_port_not_specified')
38
+ end
39
+
40
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_integer', option: :port) unless port.is_a?(Integer)
41
+
42
+ port_min = Settings.compose.port_min_value
43
+ port_max = Settings.compose.port_max_value
44
+ if port < port_min || port > port_max
45
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.port_out_of_range', port_min: port_min, port_max: port_max)
46
+ end
47
+
48
+ port
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Parsers::Services::HealthcheckParserService
4
+ REQUIRED_START_COMMANDS = ['NONE', 'CMD', 'CMD-SHELL'].freeze
5
+
6
+ class << self
7
+ def parse(healthcheck_data)
8
+ return {} if healthcheck_data.blank?
9
+
10
+ command = parse_command(healthcheck_data)
11
+
12
+ {
13
+ test: command,
14
+ interval: parse_time(healthcheck_data['interval']),
15
+ timeout: parse_time(healthcheck_data['timeout']),
16
+ retries: parse_retries(healthcheck_data['retries']),
17
+ start_period: parse_time(healthcheck_data['start_period']),
18
+ disable: parse_disable_option(healthcheck_data['disable'], command),
19
+ }
20
+ end
21
+
22
+ private
23
+
24
+ def parse_command(healthcheck_data)
25
+ command = healthcheck_data['test']
26
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.string_or_array_error', option: :test) if command.nil?
27
+
28
+ case command
29
+ when Array
30
+ start_command = command.first
31
+ raise UffizziCore::ComposeFile::ParseError unless REQUIRED_START_COMMANDS.include?(start_command)
32
+
33
+ command
34
+ when String
35
+ command
36
+ else
37
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :test)
38
+ end
39
+ end
40
+
41
+ def parse_retries(value)
42
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_integer', option: :retries) unless value.is_a?(Integer)
43
+ end
44
+
45
+ def parse_time(value)
46
+ tokens = {
47
+ 's' => 1,
48
+ 'm' => 60,
49
+ 'h' => (60 * 60),
50
+ 'd' => (60 * 60 * 24),
51
+ }
52
+
53
+ time_parts = value.scan(/(\d+)(\w)/).compact
54
+
55
+ time_parts.reduce(0) do |acc, part|
56
+ amount, measure = part
57
+ acc += amount.to_i * tokens[measure]
58
+
59
+ acc
60
+ rescue StandardError
61
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_time_interval')
62
+ end
63
+ end
64
+
65
+ def parse_disable_option(value, command)
66
+ return true if command.is_a?(Array) && command.first == 'NONE'
67
+ return false if value.nil?
68
+ return value if value.in?([true, false])
69
+
70
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_bool_value', field: 'disable', value: value)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::SecretsOptionsService
4
+ class << self
5
+ def parse(secrets_data)
6
+ return [] if secrets_data.nil?
7
+
8
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :secrets) unless secrets_data.is_a?(Hash)
9
+
10
+ secrets = []
11
+ secrets_data.each_pair do |secret_name, secret_data|
12
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.secret_name_blank', option: secret_name) if secret_data['name'].blank?
13
+
14
+ if secret_data['external'] != true
15
+ raise UffizziCore::ComposeFile::ParseError,
16
+ I18n.t('compose.secret_external', secret: secret_name)
17
+ end
18
+
19
+ secrets << {
20
+ secret_name: secret_name,
21
+ secret_variable: secret_data['name'],
22
+ }
23
+ end
24
+
25
+ secrets
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::CommandService
4
+ class << self
5
+ def parse(command_data)
6
+ return nil if command_data.blank?
7
+
8
+ case command_data
9
+ when String
10
+ [command_data]
11
+ when Array
12
+ command_data
13
+ else
14
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :command)
15
+ end
16
+ end
17
+ end
18
+ end