uffizzi_core 0.1.3

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 (240) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +201 -0
  3. data/README.md +46 -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 +139 -0
  14. data/app/clients/uffizzi_core/github/app_client.rb +19 -0
  15. data/app/clients/uffizzi_core/github/installation_client.rb +11 -0
  16. data/app/clients/uffizzi_core/github/user_client.rb +51 -0
  17. data/app/clients/uffizzi_core/google_registry_client/request_result.rb +5 -0
  18. data/app/clients/uffizzi_core/google_registry_client.rb +42 -0
  19. data/app/contexts/uffizzi_core/base_context.rb +12 -0
  20. data/app/contexts/uffizzi_core/project_context.rb +13 -0
  21. data/app/contexts/uffizzi_core/webhooks_context.rb +9 -0
  22. data/app/controllers/concerns/uffizzi_core/auth_management.rb +23 -0
  23. data/app/controllers/concerns/uffizzi_core/authorization_concern.rb +38 -0
  24. data/app/controllers/concerns/uffizzi_core/dependency_injection_concern.rb +19 -0
  25. data/app/controllers/uffizzi_core/api/cli/v1/account/application_controller.rb +7 -0
  26. data/app/controllers/uffizzi_core/api/cli/v1/account/credentials_controller.rb +55 -0
  27. data/app/controllers/uffizzi_core/api/cli/v1/application_controller.rb +5 -0
  28. data/app/controllers/uffizzi_core/api/cli/v1/projects/application_controller.rb +11 -0
  29. data/app/controllers/uffizzi_core/api/cli/v1/projects/compose_files_controller.rb +93 -0
  30. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/activity_items_controller.rb +36 -0
  31. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/application_controller.rb +7 -0
  32. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/containers/application_controller.rb +8 -0
  33. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/containers/logs_controller.rb +27 -0
  34. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/containers_controller.rb +24 -0
  35. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/events_controller.rb +29 -0
  36. data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments_controller.rb +148 -0
  37. data/app/controllers/uffizzi_core/api/cli/v1/projects/secrets_controller.rb +69 -0
  38. data/app/controllers/uffizzi_core/api/cli/v1/projects_controller.rb +19 -0
  39. data/app/controllers/uffizzi_core/api/cli/v1/sessions_controller.rb +43 -0
  40. data/app/controllers/uffizzi_core/application_controller.rb +51 -0
  41. data/app/errors/uffizzi_core/compose_file/build_error.rb +4 -0
  42. data/app/errors/uffizzi_core/compose_file/credential_error.rb +4 -0
  43. data/app/errors/uffizzi_core/compose_file/not_found_error.rb +4 -0
  44. data/app/errors/uffizzi_core/compose_file/parse_error.rb +4 -0
  45. data/app/errors/uffizzi_core/compose_file/secrets_error.rb +4 -0
  46. data/app/errors/uffizzi_core/deployment_not_found_error.rb +10 -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 +39 -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 +91 -0
  55. data/app/forms/uffizzi_core/api/cli/v1/project/delete_secret_form.rb +27 -0
  56. data/app/forms/uffizzi_core/api/cli/v1/project/update_form.rb +40 -0
  57. data/app/forms/uffizzi_core/api/cli/v1/session_create_form.rb +29 -0
  58. data/app/forms/uffizzi_core/api/cli/v1/template/create_form.rb +65 -0
  59. data/app/forms/uffizzi_core/application_form.rb +11 -0
  60. data/app/forms/uffizzi_core/application_form_without_active_record.rb +17 -0
  61. data/app/forms/uffizzi_core/mass_assignment_control_concern.rb +22 -0
  62. data/app/helpers/uffizzi_core/application_helper.rb +6 -0
  63. data/app/jobs/uffizzi_core/account/create_credential_job.rb +10 -0
  64. data/app/jobs/uffizzi_core/activity_item/docker/update_digest_job.rb +11 -0
  65. data/app/jobs/uffizzi_core/application_job.rb +7 -0
  66. data/app/jobs/uffizzi_core/config_file/apply_job.rb +31 -0
  67. data/app/jobs/uffizzi_core/credential/docker_hub/create_webhook_job.rb +15 -0
  68. data/app/jobs/uffizzi_core/deployment/create_credential_job.rb +32 -0
  69. data/app/jobs/uffizzi_core/deployment/create_credentials_job.rb +17 -0
  70. data/app/jobs/uffizzi_core/deployment/create_job.rb +15 -0
  71. data/app/jobs/uffizzi_core/deployment/create_webhooks_job.rb +13 -0
  72. data/app/jobs/uffizzi_core/deployment/delete_credential_job.rb +13 -0
  73. data/app/jobs/uffizzi_core/deployment/delete_job.rb +11 -0
  74. data/app/jobs/uffizzi_core/deployment/deploy_containers_job.rb +27 -0
  75. data/app/jobs/uffizzi_core/deployment/manage_deploy_activity_item_job.rb +19 -0
  76. data/app/jobs/uffizzi_core/deployment/send_github_preview_message_job.rb +13 -0
  77. data/app/lib/uffizzi_core/rbac/user_access_service.rb +21 -0
  78. data/app/mailers/uffizzi_core/application_mailer.rb +8 -0
  79. data/app/models/concerns/uffizzi_core/hashid_concern.rb +25 -0
  80. data/app/models/concerns/uffizzi_core/state_machine_concern.rb +16 -0
  81. data/app/models/uffizzi_core/account.rb +101 -0
  82. data/app/models/uffizzi_core/activity_item/docker.rb +4 -0
  83. data/app/models/uffizzi_core/activity_item/github.rb +4 -0
  84. data/app/models/uffizzi_core/activity_item/memory_limit.rb +4 -0
  85. data/app/models/uffizzi_core/activity_item.rb +58 -0
  86. data/app/models/uffizzi_core/application_record.rb +7 -0
  87. data/app/models/uffizzi_core/build.rb +39 -0
  88. data/app/models/uffizzi_core/comment.rb +16 -0
  89. data/app/models/uffizzi_core/compose_file.rb +57 -0
  90. data/app/models/uffizzi_core/config_file.rb +24 -0
  91. data/app/models/uffizzi_core/container.rb +100 -0
  92. data/app/models/uffizzi_core/container_config_file.rb +8 -0
  93. data/app/models/uffizzi_core/continuous_preview.rb +4 -0
  94. data/app/models/uffizzi_core/coupon.rb +5 -0
  95. data/app/models/uffizzi_core/credential/amazon.rb +4 -0
  96. data/app/models/uffizzi_core/credential/azure.rb +4 -0
  97. data/app/models/uffizzi_core/credential/docker_hub.rb +4 -0
  98. data/app/models/uffizzi_core/credential/github.rb +4 -0
  99. data/app/models/uffizzi_core/credential/google.rb +4 -0
  100. data/app/models/uffizzi_core/credential.rb +64 -0
  101. data/app/models/uffizzi_core/database.rb +4 -0
  102. data/app/models/uffizzi_core/database_offering.rb +4 -0
  103. data/app/models/uffizzi_core/deployment.rb +77 -0
  104. data/app/models/uffizzi_core/event.rb +13 -0
  105. data/app/models/uffizzi_core/invitation.rb +27 -0
  106. data/app/models/uffizzi_core/membership.rb +16 -0
  107. data/app/models/uffizzi_core/payment.rb +11 -0
  108. data/app/models/uffizzi_core/price.rb +9 -0
  109. data/app/models/uffizzi_core/product.rb +11 -0
  110. data/app/models/uffizzi_core/project.rb +66 -0
  111. data/app/models/uffizzi_core/rating.rb +20 -0
  112. data/app/models/uffizzi_core/repo/amazon.rb +4 -0
  113. data/app/models/uffizzi_core/repo/azure.rb +4 -0
  114. data/app/models/uffizzi_core/repo/docker_hub.rb +4 -0
  115. data/app/models/uffizzi_core/repo/github.rb +4 -0
  116. data/app/models/uffizzi_core/repo/google.rb +4 -0
  117. data/app/models/uffizzi_core/repo.rb +39 -0
  118. data/app/models/uffizzi_core/role.rb +17 -0
  119. data/app/models/uffizzi_core/template.rb +19 -0
  120. data/app/models/uffizzi_core/user.rb +62 -0
  121. data/app/models/uffizzi_core/user_project.rb +14 -0
  122. data/app/policies/uffizzi_core/api/cli/v1/account/credentials_policy.rb +11 -0
  123. data/app/policies/uffizzi_core/api/cli/v1/projects/compose_files_policy.rb +15 -0
  124. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments/activity_items_policy.rb +7 -0
  125. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments/containers_policy.rb +7 -0
  126. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments/events_policy.rb +7 -0
  127. data/app/policies/uffizzi_core/api/cli/v1/projects/deployments_policy.rb +23 -0
  128. data/app/policies/uffizzi_core/api/cli/v1/projects/secrets_policy.rb +15 -0
  129. data/app/policies/uffizzi_core/api/cli/v1/projects_policy.rb +7 -0
  130. data/app/policies/uffizzi_core/application_policy.rb +12 -0
  131. data/app/repositories/uffizzi_core/activity_item_repo.rb +9 -0
  132. data/app/repositories/uffizzi_core/basic_order_repo.rb +11 -0
  133. data/app/repositories/uffizzi_core/build_repo.rb +23 -0
  134. data/app/repositories/uffizzi_core/comment_repo.rb +11 -0
  135. data/app/repositories/uffizzi_core/compose_file_repo.rb +11 -0
  136. data/app/repositories/uffizzi_core/config_file_repo.rb +40 -0
  137. data/app/repositories/uffizzi_core/container_repo.rb +25 -0
  138. data/app/repositories/uffizzi_core/credential_repo.rb +36 -0
  139. data/app/repositories/uffizzi_core/deployment_repo.rb +23 -0
  140. data/app/repositories/uffizzi_core/event_repo.rb +9 -0
  141. data/app/repositories/uffizzi_core/membership_repo.rb +10 -0
  142. data/app/repositories/uffizzi_core/price_repo.rb +11 -0
  143. data/app/repositories/uffizzi_core/product_repo.rb +11 -0
  144. data/app/repositories/uffizzi_core/project_repo.rb +10 -0
  145. data/app/repositories/uffizzi_core/repo_repo.rb +10 -0
  146. data/app/repositories/uffizzi_core/template_repo.rb +87 -0
  147. data/app/repositories/uffizzi_core/usage_repo.rb +9 -0
  148. data/app/repositories/uffizzi_core/user_repo.rb +11 -0
  149. data/app/responders/uffizzi_core/json_responder.rb +13 -0
  150. data/app/serializers/uffizzi_core/api/cli/v1/account/credential_serializer.rb +9 -0
  151. data/app/serializers/uffizzi_core/api/cli/v1/project_serializer.rb +7 -0
  152. data/app/serializers/uffizzi_core/api/cli/v1/projects/compose_file_serializer.rb +7 -0
  153. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/container_serializer.rb +23 -0
  154. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/user_serializer.rb +11 -0
  155. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer.rb +74 -0
  156. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/activity_item_serializer.rb +24 -0
  157. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer/container_config_file_serializer/config_file_serializer.rb +6 -0
  158. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer/container_config_file_serializer.rb +7 -0
  159. data/app/serializers/uffizzi_core/api/cli/v1/projects/deployments/container_serializer.rb +30 -0
  160. data/app/serializers/uffizzi_core/api/cli/v1/user_serializer/account_serializer.rb +5 -0
  161. data/app/serializers/uffizzi_core/api/cli/v1/user_serializer.rb +7 -0
  162. data/app/serializers/uffizzi_core/base_serializer.rb +7 -0
  163. data/app/serializers/uffizzi_core/controller/create_credential/credential_serializer.rb +17 -0
  164. data/app/serializers/uffizzi_core/controller/create_deployment/deployment_serializer.rb +5 -0
  165. data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer.rb +53 -0
  166. data/app/serializers/uffizzi_core/controller/deploy_containers/credential_serializer.rb +5 -0
  167. data/app/services/uffizzi_core/account_service.rb +21 -0
  168. data/app/services/uffizzi_core/activity_item_service.rb +98 -0
  169. data/app/services/uffizzi_core/amazon/credential_service.rb +31 -0
  170. data/app/services/uffizzi_core/amazon_service.rb +45 -0
  171. data/app/services/uffizzi_core/azure/credential_service.rb +18 -0
  172. data/app/services/uffizzi_core/cli/compose_file_service.rb +203 -0
  173. data/app/services/uffizzi_core/compose_file/builders/config_files_builder_service.rb +31 -0
  174. data/app/services/uffizzi_core/compose_file/builders/container_builder_service.rb +225 -0
  175. data/app/services/uffizzi_core/compose_file/builders/docker_repo_builder_service.rb +25 -0
  176. data/app/services/uffizzi_core/compose_file/builders/github_repo_builder_service.rb +59 -0
  177. data/app/services/uffizzi_core/compose_file/builders/template_builder_service.rb +45 -0
  178. data/app/services/uffizzi_core/compose_file/builders/variables_builder_service.rb +58 -0
  179. data/app/services/uffizzi_core/compose_file/config_files_service.rb +52 -0
  180. data/app/services/uffizzi_core/compose_file/config_option_service.rb +37 -0
  181. data/app/services/uffizzi_core/compose_file/configs_options_service.rb +26 -0
  182. data/app/services/uffizzi_core/compose_file/container_service.rb +64 -0
  183. data/app/services/uffizzi_core/compose_file/continuous_preview_options_service.rb +57 -0
  184. data/app/services/uffizzi_core/compose_file/dependencies_service.rb +55 -0
  185. data/app/services/uffizzi_core/compose_file/errors_service.rb +46 -0
  186. data/app/services/uffizzi_core/compose_file/github_dependencies_service.rb +38 -0
  187. data/app/services/uffizzi_core/compose_file/ingress_options_service.rb +49 -0
  188. data/app/services/uffizzi_core/compose_file/secrets_options_service.rb +28 -0
  189. data/app/services/uffizzi_core/compose_file/services_options/build_service.rb +93 -0
  190. data/app/services/uffizzi_core/compose_file/services_options/command_service.rb +18 -0
  191. data/app/services/uffizzi_core/compose_file/services_options/configs_service.rb +51 -0
  192. data/app/services/uffizzi_core/compose_file/services_options/deploy_service.rb +44 -0
  193. data/app/services/uffizzi_core/compose_file/services_options/entrypoint_service.rb +18 -0
  194. data/app/services/uffizzi_core/compose_file/services_options/env_file_service.rb +34 -0
  195. data/app/services/uffizzi_core/compose_file/services_options/environment_service.rb +20 -0
  196. data/app/services/uffizzi_core/compose_file/services_options/image_service.rb +89 -0
  197. data/app/services/uffizzi_core/compose_file/services_options/secrets_service.rb +35 -0
  198. data/app/services/uffizzi_core/compose_file/services_options_service.rb +55 -0
  199. data/app/services/uffizzi_core/compose_file/template_service.rb +55 -0
  200. data/app/services/uffizzi_core/compose_file/update_service.rb +29 -0
  201. data/app/services/uffizzi_core/compose_file/variables_service.rb +25 -0
  202. data/app/services/uffizzi_core/compose_file_service.rb +33 -0
  203. data/app/services/uffizzi_core/container_service.rb +57 -0
  204. data/app/services/uffizzi_core/controller_service.rb +80 -0
  205. data/app/services/uffizzi_core/credential_service.rb +44 -0
  206. data/app/services/uffizzi_core/deployment_service.rb +274 -0
  207. data/app/services/uffizzi_core/docker_hub/credential_service.rb +15 -0
  208. data/app/services/uffizzi_core/docker_hub_service.rb +77 -0
  209. data/app/services/uffizzi_core/github/app_service.rb +51 -0
  210. data/app/services/uffizzi_core/github/credential_service.rb +124 -0
  211. data/app/services/uffizzi_core/github/message_service.rb +20 -0
  212. data/app/services/uffizzi_core/github_service.rb +28 -0
  213. data/app/services/uffizzi_core/google/credential_service.rb +18 -0
  214. data/app/services/uffizzi_core/logs_service.rb +33 -0
  215. data/app/services/uffizzi_core/manage_activity_items_service.rb +166 -0
  216. data/app/services/uffizzi_core/project_service.rb +38 -0
  217. data/app/services/uffizzi_core/repo_service.rb +178 -0
  218. data/app/services/uffizzi_core/response_service.rb +13 -0
  219. data/app/services/uffizzi_core/template_service.rb +21 -0
  220. data/app/services/uffizzi_core/token_service.rb +19 -0
  221. data/app/services/uffizzi_core/user_access_service.rb +14 -0
  222. data/app/utils/uffizzi_core/converters.rb +33 -0
  223. data/app/validators/uffizzi_core/email_validator.rb +9 -0
  224. data/app/validators/uffizzi_core/environment_variable_list_validator.rb +15 -0
  225. data/app/validators/uffizzi_core/image_command_args_validator.rb +21 -0
  226. data/config/initializers/rswag_api.rb +15 -0
  227. data/config/initializers/rswag_ui.rb +15 -0
  228. data/config/initializers/swagger_yard.rb +17 -0
  229. data/config/locales/en.activerecord.yml +18 -0
  230. data/config/locales/en.yml +61 -0
  231. data/config/routes.rb +55 -0
  232. data/db/migrate/20220218121438_create_uffizzi_core_tables.rb +375 -0
  233. data/db/migrate/20220325113342_add_name_to_uffizzi_containers.rb +7 -0
  234. data/db/seeds.rb +16 -0
  235. data/lib/tasks/uffizzi_core_tasks.rake +14 -0
  236. data/lib/uffizzi_core/engine.rb +15 -0
  237. data/lib/uffizzi_core/version.rb +5 -0
  238. data/lib/uffizzi_core.rb +60 -0
  239. data/swagger/v1/swagger.json +1278 -0
  240. metadata +935 -0
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::DependenciesService
4
+ ENV_FILE_TYPE = 'env_file'
5
+ CONFIG_TYPE = 'config'
6
+ class << self
7
+ def build_dependencies(compose_data, compose_path, dependencies_params)
8
+ dependencies = compose_data[:containers].map do |container|
9
+ env_file_dependencies = build_env_files_dependencies(container, compose_path, dependencies_params)
10
+ configs_dependencies = build_configs_dependencies(container, compose_path, dependencies_params)
11
+
12
+ env_file_dependencies + configs_dependencies
13
+ end
14
+
15
+ dependencies.compact.flatten
16
+ end
17
+
18
+ def build_env_files_dependencies(container, compose_path, dependencies_params)
19
+ env_files = container[:env_file]
20
+ return [] unless env_files.present?
21
+
22
+ env_files.map do |path|
23
+ dependency = dependencies_params.detect { |item| item[:path] == path }
24
+ source = build_source_path(compose_path, path)
25
+
26
+ base_file_params(dependency, container).merge(source: source, type: ENV_FILE_TYPE)
27
+ end
28
+ end
29
+
30
+ def build_configs_dependencies(container, compose_path, dependencies_params)
31
+ configs = container[:configs]
32
+ return [] unless configs.present?
33
+
34
+ configs.map do |config|
35
+ dependency = dependencies_params.detect { |item| item[:path] == config[:source] }
36
+ source = build_source_path(compose_path, dependency[:path])
37
+
38
+ base_file_params(dependency, container).merge(source: source, type: CONFIG_TYPE)
39
+ end
40
+ end
41
+
42
+ def base_file_params(dependency, container)
43
+ {
44
+ content: dependency[:content],
45
+ path: dependency[:path],
46
+ container_name: container[:container_name],
47
+ }
48
+ end
49
+
50
+ def build_source_path(compose_path, dependency_path)
51
+ prepared_compose_path = Pathname.new(compose_path).basename.to_s
52
+ "#{prepared_compose_path}/#{dependency_path}"
53
+ end
54
+ end
55
+ 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,49 @@
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
+ if port < Settings.compose.port_min_value || port > Settings.compose.port_max_value
43
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_port')
44
+ end
45
+
46
+ port
47
+ end
48
+ end
49
+ 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,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ServicesOptions::BuildService
4
+ extend UffizziCore::ComposeFile::VariablesService
5
+
6
+ class << self
7
+ def parse(build_data, compose_payload)
8
+ return {} if build_data.blank?
9
+
10
+ args = parse_args(build_data)
11
+
12
+ repository_url, branch, dockerfile_context_path = parse_context(build_data, compose_payload)
13
+ repository_parts = repository_url.split('/').last(2)
14
+ account_name = repository_parts.first
15
+ repository_name = repository_parts.last
16
+ dockerfile = build_data['dockerfile'].blank? ? ::Settings.compose.dockerfile_default_path : build_data['dockerfile']
17
+
18
+ {
19
+ repository_url: repository_url,
20
+ account_name: account_name,
21
+ repository_name: repository_name,
22
+ branch: branch,
23
+ dockerfile: dockerfile,
24
+ dockerfile_context_path: dockerfile_context_path,
25
+ args: args,
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def parse_args(build_data)
32
+ args = build_data['args']
33
+ return [] if args.nil?
34
+
35
+ case args
36
+ when Array
37
+ args.map { |arg| parse_variable_from_string(arg) }
38
+ when Hash
39
+ args.to_a.map { |arg| parse_variable_from_array(arg) }
40
+ else
41
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :args)
42
+ end
43
+ end
44
+
45
+ def parse_context(build_data, compose_payload)
46
+ if build_data.is_a?(String)
47
+ parse_context_from_inline_syntax(build_data, compose_payload)
48
+ else
49
+ parse_context_from_long_syntax(build_data, compose_payload)
50
+ end
51
+ end
52
+
53
+ def parse_repository_url_fragment(fragment)
54
+ branch, dockerfile_context_path = fragment.split(':')
55
+
56
+ [branch, dockerfile_context_path]
57
+ end
58
+
59
+ def valid_url?(url)
60
+ URI(url).host.present? && URI(url).host =~ /\w+\.\w+/ && URI(url).path.present?
61
+ end
62
+
63
+ def parse_context_from_inline_syntax(context, compose_payload)
64
+ if compose_payload[:repository_url].blank?
65
+ raise UffizziCore::ComposeFile::ParseError,
66
+ I18n.t('compose.build_context_unknown_repository')
67
+ end
68
+
69
+ repository_url = compose_payload[:repository_url]
70
+ branch = compose_payload[:branch]
71
+ dockerfile_context_path = UffizziCore::ComposeFile::ConfigOptionService.prepare_file_path_value(context)
72
+
73
+ [repository_url, branch, dockerfile_context_path]
74
+ end
75
+
76
+ def parse_context_from_long_syntax(build_data, compose_payload)
77
+ context = build_data['context']
78
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.build_context_no_specified') if context.blank?
79
+
80
+ if valid_url?(context)
81
+ uri = URI(context)
82
+ repository_url = "#{uri.scheme}://#{uri.host}#{uri.path}"
83
+ branch, dockerfile_context_path = parse_repository_url_fragment(uri.fragment.to_s)
84
+ else
85
+ repository_url, branch, dockerfile_context_path = parse_context_from_inline_syntax(context, compose_payload)
86
+ end
87
+
88
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_context', value: context) if repository_url.blank?
89
+
90
+ [repository_url, branch, dockerfile_context_path]
91
+ end
92
+ end
93
+ 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
@@ -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,55 @@
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
+ UffizziCore::ComposeFile::ServicesOptions::BuildService.parse(value, compose_payload)
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 :'x-uffizzi-continuous-preview', :'x-uffizzi-continuous-previews'
46
+ UffizziCore::ComposeFile::ContinuousPreviewOptionsService.parse(value)
47
+ end
48
+ end
49
+
50
+ options_data[:container_name] = service_name
51
+
52
+ options_data
53
+ end
54
+ end
55
+ end