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,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Builders::ConfigFilesBuilderService
4
+ attr_accessor :project
5
+
6
+ def initialize(project)
7
+ @project = project
8
+ end
9
+
10
+ def build_attributes(config_files_data, dependencies)
11
+ return [] if config_files_data.empty?
12
+
13
+ config_file_sources = dependencies.pluck(:source)
14
+ config_files = project.config_files.with_creation_source(UffizziCore::ConfigFile.creation_source.compose_file)
15
+ .by_source(config_file_sources)
16
+
17
+ config_files_data.map do |config_file_data|
18
+ detected_dependency = dependencies.detect { |dependency| dependency[:path] == config_file_data[:source] }
19
+ detected_config_file = config_files.detect { |config_file| config_file.source == detected_dependency[:source] }
20
+
21
+ if detected_config_file.nil?
22
+ raise UffizziCore::ComposeFile::BuildError, I18n.t('compose.config_file_not_found', name: config_file_data[:source])
23
+ end
24
+
25
+ {
26
+ mount_path: config_file_data[:target],
27
+ config_file_id: detected_config_file.id,
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
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(container_data, ingress_data, continuous_preview_global_data, compose_dependencies)
13
+ image_data = container_data[:image] || {}
14
+ build_data = container_data[:build] || {}
15
+ environment = container_data[:environment] || []
16
+ deploy_data = container_data[:deploy] || {}
17
+ configs_data = container_data[:configs] || []
18
+ secrets = container_data[:secrets] || []
19
+ container_name = container_data[:container_name]
20
+ continuous_preview_container_data = container_data[:'x-uffizzi-continuous-preview'] || container_data[:'x-uffizzi-continuous-previews']
21
+
22
+ env_file_dependencies = UffizziCore::ComposeFile::GithubDependenciesService.env_file_dependencies_for_container(compose_dependencies,
23
+ container_name)
24
+ configs_dependencies = UffizziCore::ComposeFile::GithubDependenciesService.configs_dependencies_for_container(compose_dependencies,
25
+ container_name)
26
+
27
+ is_ingress = ingress_container?(container_name, ingress_data)
28
+ repo_attributes = build_repo_attributes(container_data, image_data, build_data, credentials)
29
+
30
+ repo_attributes = set_continuous_preview_attributes_to_repo(repo_attributes, continuous_preview_global_data.to_h,
31
+ continuous_preview_container_data.to_h)
32
+
33
+ {
34
+ tag: tag(image_data, repo_attributes),
35
+ port: port(container_name, ingress_data),
36
+ image: image(container_data, image_data, build_data),
37
+ public: is_ingress,
38
+ entrypoint: entrypoint(container_data),
39
+ command: command(container_data),
40
+ variables: variables(environment, env_file_dependencies),
41
+ secret_variables: secret_variables(secrets),
42
+ memory_limit: memory(deploy_data),
43
+ memory_request: memory(deploy_data),
44
+ repo_attributes: repo_attributes,
45
+ continuously_deploy: continuously_deploy(deploy_data),
46
+ receive_incoming_requests: is_ingress,
47
+ container_config_files_attributes: config_files(configs_data, configs_dependencies),
48
+ name: container_name,
49
+ }
50
+ end
51
+
52
+ private
53
+
54
+ def set_continuous_preview_attributes_to_repo(repo_attributes, global_data, container_data)
55
+ condition_attributes = [
56
+ :deploy_preview_when_pull_request_is_opened,
57
+ :delete_preview_when_pull_request_is_closed,
58
+ :deploy_preview_when_image_tag_is_created,
59
+ :delete_preview_when_image_tag_is_updated,
60
+ :share_to_github,
61
+ ]
62
+
63
+ condition_attributes.each do |attribute|
64
+ repo_attributes[attribute] = select_continuous_preview_attribute(global_data[attribute], container_data[attribute], false)
65
+ end
66
+ repo_attributes[:delete_preview_after] =
67
+ select_continuous_preview_attribute(global_data.dig(:delete_preview_after, :value),
68
+ container_data.dig(:delete_preview_after, :value), nil)
69
+
70
+ repo_attributes
71
+ end
72
+
73
+ def select_continuous_preview_attribute(global_attribute, local_attribute, default_attribute)
74
+ return local_attribute if !local_attribute.nil?
75
+ return global_attribute if !global_attribute.nil?
76
+
77
+ default_attribute
78
+ end
79
+
80
+ def tag(image_data, repo_attributes)
81
+ image_data[:tag] || repo_attributes[:branch]
82
+ end
83
+
84
+ def port(container_name, ingress)
85
+ return nil unless ingress_container?(container_name, ingress)
86
+
87
+ ingress[:port]
88
+ end
89
+
90
+ def image(container_data, image_data, build_data)
91
+ if image_data.present?
92
+ image_name(container_data, image_data)
93
+ else
94
+ "#{build_data[:account_name]}/#{build_data[:repository_name]}"
95
+ end
96
+ end
97
+
98
+ def image_name(container_data, image_data)
99
+ if image_data[:registry_url].present? && !UffizziCore::ComposeFile::ContainerService.google?(container_data)
100
+ image_data[:name]
101
+ else
102
+ "#{image_data[:namespace]}/#{image_data[:name]}"
103
+ end
104
+ end
105
+
106
+ def ingress_container?(container_name, ingress)
107
+ ingress[:container_name] == container_name
108
+ end
109
+
110
+ def entrypoint(container_data)
111
+ entrypoint = container_data[:entrypoint]
112
+
113
+ entrypoint.present? ? entrypoint.to_s : nil
114
+ end
115
+
116
+ def command(container_data)
117
+ command = container_data[:command]
118
+
119
+ command.present? ? command.to_s : nil
120
+ end
121
+
122
+ def command_args(command_data)
123
+ return nil if command_data[:command_args].blank?
124
+
125
+ command_data[:command_args].to_s
126
+ end
127
+
128
+ def memory(deploy_data)
129
+ memory = deploy_data[:memory]
130
+ return Settings.compose.default_memory if memory.nil?
131
+
132
+ memory_value = case memory[:postfix]
133
+ when 'b'
134
+ memory[:value] / 1_000_000
135
+ when 'k'
136
+ memory[:value] / 1000
137
+ when 'm'
138
+ memory[:value]
139
+ when 'g'
140
+ memory[:value] * 1000
141
+ end
142
+
143
+ unless Settings.compose.memory_values.include?(memory_value)
144
+ raise UffizziCore::ComposeFile::BuildError,
145
+ I18n.t('compose.invalid_memory')
146
+ end
147
+
148
+ memory_value
149
+ end
150
+
151
+ def build_repo_attributes(container_data, image_data, build_data, credentials)
152
+ repo_type = repo_type(container_data)
153
+
154
+ case repo_type
155
+ when UffizziCore::Repo::Github.name
156
+ build_github_repo_attributes(build_data, credentials)
157
+ when UffizziCore::Repo::DockerHub.name
158
+ build_docker_repo_attributes(image_data, credentials, :docker_hub, UffizziCore::Repo::DockerHub.name)
159
+ when UffizziCore::Repo::Azure.name
160
+ build_docker_repo_attributes(image_data, credentials, :azure, UffizziCore::Repo::Azure.name)
161
+ when UffizziCore::Repo::Google.name
162
+ build_docker_repo_attributes(image_data, credentials, :google, UffizziCore::Repo::Google.name)
163
+ else
164
+ raise UffizziCore::ComposeFile::BuildError, I18n.t('compose.invalid_repo_type')
165
+ end
166
+ end
167
+
168
+ def repo_type(container_data)
169
+ if UffizziCore::ComposeFile::ContainerService.github?(container_data)
170
+ UffizziCore::Repo::Github.name
171
+ elsif UffizziCore::ComposeFile::ContainerService.azure?(container_data)
172
+ UffizziCore::Repo::Azure.name
173
+ elsif UffizziCore::ComposeFile::ContainerService.docker_hub?(container_data)
174
+ UffizziCore::Repo::DockerHub.name
175
+ elsif UffizziCore::ComposeFile::ContainerService.google?(container_data)
176
+ UffizziCore::Repo::Google.name
177
+ end
178
+ end
179
+
180
+ def continuously_deploy(deploy_data)
181
+ return :disabled if deploy_data[:auto] == false
182
+
183
+ :enabled
184
+ end
185
+
186
+ def build_github_repo_attributes(build_data, credentials)
187
+ credential = credentials.github.first
188
+ raise UffizziCore::ComposeFile::BuildError, 'Invalid credential: GitHub' if credential.nil?
189
+
190
+ github_builder.build_attributes(build_data)
191
+ end
192
+
193
+ def build_docker_repo_attributes(image_data, credentials, scope, repo_type)
194
+ credential = credentials.send(scope).first
195
+ raise UffizziCore::ComposeFile::BuildError, 'Invalid credential: Docker' if credential.nil?
196
+
197
+ docker_builder(repo_type).build_attributes(image_data)
198
+ end
199
+
200
+ def variables(variables_data, dependencies)
201
+ variables_builder.build_attributes(variables_data, dependencies)
202
+ end
203
+
204
+ def secret_variables(secrets)
205
+ variables_builder.build_secret_attributes(secrets)
206
+ end
207
+
208
+ def config_files(config_files_data, dependencies)
209
+ builder = UffizziCore::ComposeFile::Builders::ConfigFilesBuilderService.new(project)
210
+
211
+ builder.build_attributes(config_files_data, dependencies)
212
+ end
213
+
214
+ def github_builder
215
+ @github_builder ||= UffizziCore::ComposeFile::Builders::GithubRepoBuilderService.new(repositories)
216
+ end
217
+
218
+ def docker_builder(type)
219
+ @docker_builder ||= UffizziCore::ComposeFile::Builders::DockerRepoBuilderService.new(type)
220
+ end
221
+
222
+ def variables_builder
223
+ @variables_builder ||= UffizziCore::ComposeFile::Builders::VariablesBuilderService.new(project)
224
+ end
225
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Builders::DockerRepoBuilderService
4
+ attr_accessor :type
5
+
6
+ def initialize(type)
7
+ @type = type
8
+ end
9
+
10
+ def build_attributes(image_data)
11
+ {
12
+ kind: nil,
13
+ name: image_data[:name],
14
+ slug: image_data[:name],
15
+ type: type,
16
+ branch: nil,
17
+ namespace: image_data[:namespace],
18
+ is_private: nil, # TODO: detect
19
+ description: '',
20
+ repository_id: nil,
21
+ dockerfile_path: '',
22
+ dockerfile_context_path: nil,
23
+ }
24
+ end
25
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Builders::GithubRepoBuilderService
4
+ attr_accessor :repositories
5
+
6
+ def initialize(repositories)
7
+ @repositories = repositories
8
+ end
9
+
10
+ def build_attributes(build_data)
11
+ repository_data = repository(build_data[:repository_name])
12
+
13
+ {
14
+ kind: repository_kind(build_data),
15
+ name: build_data[:repository_name],
16
+ slug: build_data[:repository_name],
17
+ type: UffizziCore::Repo::Github.name,
18
+ branch: branch(build_data, repository_data),
19
+ namespace: build_data[:account_name],
20
+ is_private: nil,
21
+ description: repository_data[:description],
22
+ repository_id: repository_data[:id],
23
+ dockerfile_path: build_data[:dockerfile],
24
+ dockerfile_context_path: build_data[:dockerfile_context_path],
25
+ args: args(build_data),
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def branch(build_data, repository_data)
32
+ build_data[:branch] || repository_data[:default_branch]
33
+ end
34
+
35
+ def repository(name)
36
+ repo = repositories.detect { |repository| repository.name.downcase == name.downcase }
37
+
38
+ raise UffizziCore::ComposeFile::BuildError, I18n.t('compose.repo_not_found', name: name) if repo.nil?
39
+
40
+ repo
41
+ end
42
+
43
+ def repository_kind(build_data)
44
+ if build_data[:dockerfile].present?
45
+ UffizziCore::Repo.kind.dockerfile
46
+ else
47
+ UffizziCore::Repo.kind.barestatic
48
+ end
49
+ end
50
+
51
+ def args(build_data)
52
+ build_data[:args].map do |arg|
53
+ {
54
+ name: arg[:name],
55
+ value: arg[:value],
56
+ }
57
+ end
58
+ end
59
+ end
@@ -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,37 @@
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
+ keys << key
16
+ keys.concat(config_options(value)) if value.is_a?(Hash)
17
+ end
18
+ end
19
+
20
+ def prepare_file_path_value(file_path)
21
+ pathname = Pathname.new(file_path)
22
+
23
+ pathname.cleanpath.to_s.strip.delete_prefix('/')
24
+ end
25
+
26
+ def ingress_option(compose_data)
27
+ compose_data.dig('x-uffizzi', 'ingress').presence || compose_data['x-uffizzi-ingress'].presence
28
+ end
29
+
30
+ def continuous_preview_option(compose_data)
31
+ compose_data.dig('x-uffizzi', 'continuous_preview').presence ||
32
+ compose_data.dig('x-uffizzi', 'continuous_previews').presence ||
33
+ compose_data['x-uffizzi-continuous-preview'].presence ||
34
+ compose_data['x-uffizzi-continuous-previews'].presence
35
+ end
36
+ end
37
+ 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,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::ContainerService
4
+ class << self
5
+ def github?(container)
6
+ repository_url = container.dig(:build, :repository_url)
7
+
8
+ repository_url.present? && repository_url.include?('github.com')
9
+ end
10
+
11
+ def azure?(container)
12
+ registry_url = container.dig(:image, :registry_url)
13
+
14
+ registry_url.present? && registry_url.include?('azurecr.io')
15
+ end
16
+
17
+ def google?(container)
18
+ registry_url = container.dig(:image, :registry_url)
19
+
20
+ registry_url.present? && registry_url.include?('gcr.io')
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 has_secret?(container, secret)
31
+ container['secret_variables'].any? { |container_secret| container_secret['name'] == secret['name'] }
32
+ end
33
+
34
+ def update_secret(container, secret)
35
+ secret_index = container['secret_variables'].find_index { |container_secret| container_secret['name'] == secret['name'] }
36
+ container['secret_variables'][secret_index] = secret
37
+
38
+ container
39
+ end
40
+
41
+ def credential_for_container(container, credentials)
42
+ if UffizziCore::ComposeFile::ContainerService.github?(container)
43
+ detect_credential(credentials, :github)
44
+ elsif UffizziCore::ComposeFile::ContainerService.azure?(container)
45
+ detect_credential(credentials, :azure)
46
+ elsif UffizziCore::ComposeFile::ContainerService.docker_hub?(container)
47
+ detect_credential(credentials, :docker_hub)
48
+ elsif UffizziCore::ComposeFile::ContainerService.google?(container)
49
+ detect_credential(credentials, :google)
50
+ end
51
+ end
52
+
53
+ def detect_credential(credentials, type)
54
+ credential = credentials.detect do |item|
55
+ item.send("#{type}?")
56
+ end
57
+
58
+ error_message = "Invalid credential: #{type}"
59
+ raise UffizziCore::ComposeFile::CredentialError.new(error_message) if credential.nil?
60
+
61
+ credential
62
+ end
63
+ end
64
+ 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