uffizzi_core 0.4.1 → 0.6.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a6a5a5e07ef682bca2fc1b22a628cc093282f720f3a98a18ee290d84a05c5fc
4
- data.tar.gz: 50e1ec427b2cd4227f357b57707f9151edb2e2db62a77ee8e7f149652bd66e7d
3
+ metadata.gz: 906801fea12b493de551c88a90e6a8fb576d76e07cca94d599387a620dcca920
4
+ data.tar.gz: e42b3989f0cee2e2ca4a87c7231d477cffc426d73ba26368a5ed47a60ac2c569
5
5
  SHA512:
6
- metadata.gz: 0b69cbdcaa12a2800e80f5f6784813333b88fffbc1df28792cbc1c07ed78a6ba6e4ee0d7a38372db46ae99d22be36d2ef88377158145829ea2153901542f5f00
7
- data.tar.gz: e121d4b060d698b4d3e07256ce7a977ec14d62a727ebd5ed9127b66d4a74ee4d29bf22327084573ae13b6d25d9a2111276610644d1f61fbb078dfa4ef01859a3
6
+ metadata.gz: 7ccf182b08308c44d6b62c00832cefd031b34abad8b7c5eace999d6c34ec43976b5d2c577c5044bcf83299cef80776a55a9ebdd56a4dc82e12013bee47dd9582
7
+ data.tar.gz: 4e6a3c3921e8d360a73eeeb615c24039b362148a9f4119f04e0bfa7f8f9cff3ab8b27ed9f5923d033d482cd668f65e669a9af60609f2b060a0ed9276b56cdee2
@@ -43,34 +43,6 @@ class UffizziCore::DockerHubClient
43
43
  RequestResult.new(result: response.body)
44
44
  end
45
45
 
46
- def get_webhooks(slug:, registry:)
47
- url = BASE_URL + "/v2/repositories/#{slug}/webhook_pipeline/"
48
-
49
- response = connection.get(url, { registry: registry, page_size: 100 }) do |request|
50
- request.headers['Authorization'] = "JWT #{jwt}"
51
- end
52
-
53
- RequestResult.new(status: response.status, result: response.body)
54
- end
55
-
56
- def create_webhook(slug:, name:, expect_final_callback:, webhooks:)
57
- raise NotAuthorizedError if !authentificated?
58
-
59
- url = BASE_URL + "/v2/repositories/#{slug}/webhook_pipeline/"
60
-
61
- params = {
62
- name: name,
63
- expect_final_callback: expect_final_callback,
64
- webhooks: webhooks,
65
- }
66
-
67
- response = connection.post(url, params) do |request|
68
- request.headers['Authorization'] = "JWT #{jwt}"
69
- end
70
-
71
- RequestResult.new(status: response.status, result: response.body)
72
- end
73
-
74
46
  def accounts
75
47
  raise NotAuthorizedError if !authentificated?
76
48
 
@@ -115,16 +87,6 @@ class UffizziCore::DockerHubClient
115
87
  RequestResult.new(result: response.body)
116
88
  end
117
89
 
118
- def send_webhook_answer(url, params)
119
- conn = Faraday.new do |c|
120
- c.request(:json)
121
- c.adapter(Faraday.default_adapter)
122
- end
123
- response = conn.post(url, params)
124
-
125
- RequestResult.quiet.new(result: response.body)
126
- end
127
-
128
90
  def authentificated?
129
91
  jwt.present?
130
92
  end
@@ -7,10 +7,12 @@ module UffizziCore::DependencyInjectionConcern
7
7
  UffizziCore::UserAccessService.new(module_class(:rbac))
8
8
  end
9
9
 
10
- def build_parser_module
11
- return unless module_exists?(:github)
10
+ def find_build_parser_module
11
+ module_class(:build_parser)
12
+ end
12
13
 
13
- module_class(:github)
14
+ def find_volume_parser_module
15
+ module_class(:volume_parser)
14
16
  end
15
17
 
16
18
  def password_protection_module
@@ -8,6 +8,7 @@ class UffizziCore::Api::Cli::V1::ComposeFile::CliForm
8
8
  attribute :compose_dependencies, Array
9
9
  attribute :compose_repositories, Array
10
10
  attribute :content, String
11
+ attribute :source_kind, Symbol
11
12
 
12
13
  validates :content, presence: true
13
14
 
@@ -19,7 +19,7 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
19
19
  secrets = container_data[:secrets] || []
20
20
  container_name = container_data[:container_name]
21
21
  healthcheck_data = container_data[:healthcheck] || {}
22
- volumes = container_data[:volumes] || []
22
+ volumes_data = container_data[:volumes] || []
23
23
 
24
24
  env_file_dependencies = UffizziCore::ComposeFile::GithubDependenciesService.env_file_dependencies_for_container(compose_dependencies,
25
25
  container_name)
@@ -46,7 +46,7 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
46
46
  service_name: container_name,
47
47
  name: container_name,
48
48
  healthcheck: healthcheck_data,
49
- volumes: volumes,
49
+ volumes: volumes_data,
50
50
  }
51
51
  end
52
52
  # rubocop:enable Metrics/PerceivedComplexity
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- class UffizziCore::ComposeFile::Parsers::Services::VolumesService
3
+ class UffizziCore::ComposeFile::Parsers::Services::VolumesParserService
4
4
  HOST_VOLUME_TYPE = :host
5
5
  NAMED_VOLUME_TYPE = :named
6
6
  ANONYMOUS_VOLUME_TYPE = :anonymous
@@ -8,15 +8,15 @@ class UffizziCore::ComposeFile::Parsers::Services::VolumesService
8
8
  READ_WRITE_OPTION = 'rw'
9
9
 
10
10
  class << self
11
- def parse(volumes, named_volumes_names, service_name)
11
+ def parse(volumes, volumes_payload)
12
12
  return [] if volumes.blank?
13
13
 
14
14
  volumes.map do |volume|
15
15
  volume_data = case volume
16
16
  when String
17
- process_short_syntax(volume, named_volumes_names, service_name)
17
+ process_short_syntax(volume, volumes_payload)
18
18
  when Hash
19
- process_long_syntax(volume, named_volumes_names, service_name)
19
+ process_long_syntax(volume, volumes_payload)
20
20
  else
21
21
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :volumes)
22
22
  end
@@ -27,48 +27,49 @@ class UffizziCore::ComposeFile::Parsers::Services::VolumesService
27
27
 
28
28
  private
29
29
 
30
- def process_short_syntax(volume_data, named_volumes_names, service_name)
30
+ def process_short_syntax(volume_data, volumes_payload)
31
31
  volume_parts = volume_data.split(':').map(&:strip)
32
- has_read_only = volume_parts.include?(READONLY_OPTION)
32
+ read_only = volume_parts.last.to_s.downcase == READONLY_OPTION
33
33
  part1, part2 = volume_parts
34
34
  source_path = part1
35
35
  target_path = [READONLY_OPTION, READ_WRITE_OPTION].include?(part2.to_s.downcase) ? nil : part2
36
36
 
37
37
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_prop_is_required', prop_name: 'source') if source_path.blank?
38
38
 
39
- volume_type = volume_type(source_path, target_path)
40
-
41
- check_named_volume_existence(source_path, target_path, named_volumes_names, service_name) if volume_type == NAMED_VOLUME_TYPE
42
-
43
- {
44
- source: source_path,
45
- target: target_path,
46
- type: volume_type,
47
- read_only: has_read_only,
48
- }
39
+ build_volume_attributes(source_path, target_path, read_only, volumes_payload)
49
40
  end
50
41
 
51
- def process_long_syntax(volume_data, named_volumes_names, service_name)
42
+ def process_long_syntax(volume_data, volumes_payload)
52
43
  source_path = volume_data['source'].to_s.strip
53
44
  target_path = volume_data['target'].to_s.strip
54
- has_read_only = volume_data['read_only'].present?
45
+ read_only = volume_data['read_only'].present?
55
46
 
56
47
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_prop_is_required', prop_name: 'source') if source_path.blank?
57
48
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_prop_is_required', prop_name: 'target') if target_path.blank?
58
49
 
59
- volume_type = volume_type(source_path, target_path)
50
+ build_volume_attributes(source_path, target_path, read_only, volumes_payload)
51
+ end
60
52
 
61
- check_named_volume_existence(source_path, target_path, named_volumes_names, service_name) if volume_type == NAMED_VOLUME_TYPE
53
+ def build_volume_attributes(source_path, target_path, read_only, params = {})
54
+ volume_type = build_volume_type(source_path, target_path)
55
+
56
+ if volume_type == NAMED_VOLUME_TYPE
57
+ validate_named_volume(source_path, target_path, params[:named_volumes_names], params[:service_name])
58
+ end
59
+
60
+ if volume_type == ANONYMOUS_VOLUME_TYPE
61
+ validate_anonymous_volume(source_path)
62
+ end
62
63
 
63
64
  {
64
65
  source: source_path,
65
66
  target: target_path,
66
67
  type: volume_type,
67
- read_only: has_read_only,
68
+ read_only: read_only,
68
69
  }
69
70
  end
70
71
 
71
- def volume_type(source_path, target_path)
72
+ def build_volume_type(source_path, target_path)
72
73
  if path?(source_path) && path?(target_path)
73
74
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_type_not_supported', type: HOST_VOLUME_TYPE)
74
75
  end
@@ -83,12 +84,26 @@ class UffizziCore::ComposeFile::Parsers::Services::VolumesService
83
84
  /^(\/|\.\/|~\/)/.match?(path)
84
85
  end
85
86
 
86
- def check_named_volume_existence(source_path, target_path, named_volumes_names, service_name)
87
+ def validate_named_volume(source_path, target_path, named_volumes_names, service_name)
88
+ if path_has_only_root?(target_path)
89
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_volume_destination', spec: "#{source_path}:#{target_path}")
90
+ end
91
+
87
92
  return if named_volumes_names.include?(source_path)
88
93
 
89
94
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.named_volume_not_exists', source_path: source_path,
90
95
  target_path: target_path,
91
96
  service_name: service_name)
92
97
  end
98
+
99
+ def validate_anonymous_volume(path)
100
+ if path_has_only_root?(path)
101
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_volume_destination', spec: path)
102
+ end
103
+ end
104
+
105
+ def path_has_only_root?(path)
106
+ path.size == 1 && path.include?('/')
107
+ end
93
108
  end
94
109
  end
@@ -51,9 +51,9 @@ class UffizziCore::ComposeFile::Parsers::ServicesParserService
51
51
  when :'x-uffizzi-continuous-preview', :'x-uffizzi-continuous-previews'
52
52
  UffizziCore::ComposeFile::Parsers::ContinuousPreviewParserService.parse(value)
53
53
  when :volumes
54
- UffizziCore::ComposeFile::Parsers::Services::VolumesService.parse(value,
55
- global_named_volume_names,
56
- service_name)
54
+ parse_volumes(value, named_volumes_names: global_named_volume_names,
55
+ service_name: service_name,
56
+ compose_payload: compose_payload)
57
57
  end
58
58
  end
59
59
 
@@ -63,9 +63,19 @@ class UffizziCore::ComposeFile::Parsers::ServicesParserService
63
63
  end
64
64
 
65
65
  def check_and_parse_build_option(value, compose_payload)
66
+ build_parser_module = find_build_parser_module
67
+
66
68
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.not_implemented', option: :build) unless build_parser_module
67
69
 
68
70
  build_parser_module.parse(value, compose_payload)
69
71
  end
72
+
73
+ def parse_volumes(value, volumes_payload)
74
+ volume_parser_module = find_volume_parser_module
75
+
76
+ return UffizziCore::ComposeFile::Parsers::Services::VolumesParserService.parse(value, volumes_payload) unless volume_parser_module
77
+
78
+ volume_parser_module.parse(value, volumes_payload)
79
+ end
70
80
  end
71
81
  end
@@ -23,7 +23,6 @@ class UffizziCore::DeploymentService
23
23
  update_subdomain!(deployment_form)
24
24
 
25
25
  UffizziCore::Deployment::CreateJob.perform_async(deployment_form.id)
26
- UffizziCore::Deployment::CreateWebhooksJob.perform_async(deployment_form.id)
27
26
  end
28
27
 
29
28
  deployment_form
@@ -38,9 +37,13 @@ class UffizziCore::DeploymentService
38
37
 
39
38
  ActiveRecord::Base.transaction do
40
39
  deployment.containers.destroy_all
41
- deployment.compose_file.destroy! if deployment.compose_file.kind.temporary?
40
+ deployment.compose_file.destroy! if deployment.compose_file&.kind&.temporary?
42
41
 
43
- deployment.update!(containers: deployment_form.containers, compose_file_id: compose_file.id)
42
+ deployment.update!(
43
+ containers: deployment_form.containers,
44
+ compose_file_id: compose_file.id,
45
+ creation_source: UffizziCore::Deployment.creation_source.compose_file_manual,
46
+ )
44
47
  end
45
48
 
46
49
  deployment
@@ -204,28 +207,6 @@ class UffizziCore::DeploymentService
204
207
  deployment.save!
205
208
  end
206
209
 
207
- def create_webhooks(deployment)
208
- credential = deployment.project.account.credentials.docker_hub.active.first
209
-
210
- if !deployment.containers.with_docker_hub_repo.exists? || !credential.present?
211
- Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment.id} create_webhooks no dockerhub containers or credential")
212
- return
213
- end
214
-
215
- accounts = UffizziCore::DockerHubService.accounts(credential)
216
-
217
- deployment.containers.with_docker_hub_repo.find_each do |container|
218
- if !accounts.include?(container.repo.namespace)
219
- logger_message = "DEPLOYMENT_PROCESS deployment_id=#{deployment.id} no namespace(#{container.repo.namespace})
220
- in accounts(#{accounts.inspect})"
221
- Rails.logger.info(logger_message)
222
- next
223
- end
224
-
225
- UffizziCore::Credential::DockerHub::CreateWebhookJob.perform_async(credential.id, container.image, deployment.id)
226
- end
227
- end
228
-
229
210
  def pull_request_payload_present?(deployment)
230
211
  deployment.continuous_preview_payload.present? && deployment.continuous_preview_payload['pull_request'].present?
231
212
  end
@@ -304,7 +285,10 @@ class UffizziCore::DeploymentService
304
285
  subdomain_length_limit = Settings.deployment.subdomain.length_limit
305
286
  return rfc_subdomain if rfc_subdomain.length <= subdomain_length_limit
306
287
 
307
- rfc_subdomain.slice(0, subdomain_length_limit)
288
+ sliced_subdomain = rfc_subdomain.slice(0, subdomain_length_limit)
289
+ return sliced_subdomain.chop if sliced_subdomain.end_with?('-')
290
+
291
+ sliced_subdomain
308
292
  end
309
293
  end
310
294
  end
@@ -1,44 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UffizziCore::DockerHubService
4
- HOOK_NAME = 'Uffizzi OpenSource Deploy Webhook'
5
- HOOK_URL = "#{Settings.app.host}api/cli/v1/webhooks/docker_hub"
6
- REGISTRY = 'registry-1.docker.io'
7
-
8
4
  class << self
9
- def create_webhook(credential, image)
10
- client = user_client(credential)
11
-
12
- webhooks_response = client.get_webhooks(slug: image, registry: REGISTRY)
13
- Rails.logger.info("DockerHubService create_webhook get_webhooks_response=#{webhooks_response.inspect}")
14
-
15
- return false if webhooks_response.status != 200
16
-
17
- webhooks = webhooks_response.result['results']
18
-
19
- webhook = webhooks.detect { |hook| hook['name'] == HOOK_NAME }
20
-
21
- return true if !webhook.nil?
22
-
23
- params = {
24
- slug: image,
25
- name: HOOK_NAME,
26
- expect_final_callback: false,
27
- webhooks: [{ name: HOOK_NAME, hook_url: HOOK_URL, registry: REGISTRY }],
28
- }
29
-
30
- response = client.create_webhook(params)
31
-
32
- Rails.logger.info("DockerHubService create_webhook create_webhook_response=#{response.inspect} params=#{params.inspect}")
33
-
34
- response.status == 201
35
- end
36
-
37
- def send_webhook_answer(callback_url)
38
- params = { state: 'success', description: 'Successfully deployed to Uffizzi' }
39
- public_docker_hub_client.send_webhook_answer(callback_url, params)
40
- end
41
-
42
5
  def accounts(credential)
43
6
  client = user_client(credential)
44
7
  response = client.accounts
@@ -66,6 +66,7 @@ en:
66
66
  volume_invalid_name: "Volumes value '%{name}' does not match any of the regexes: '^[a-zA-Z0-9._-]+$'"
67
67
  volume_type_not_supported: Volumes with type '%{type}' does not supported
68
68
  named_volume_not_exists: Named volume '%{source_path}:%{target_path}' is used in service '%{service_name}' but no declaration was found in the volumes section.
69
+ invalid_volume_destination: Invalid volume specification '%{spec}' destination can't be '/'
69
70
  secrets:
70
71
  duplicates_exists: Secret with key %{secrets} already exist.
71
72
  invalid_key_length: A secret key must be no longer than 256 characters.
data/config/routes.rb CHANGED
@@ -7,14 +7,6 @@ UffizziCore::Engine.routes.draw do
7
7
  namespace :api, defaults: { format: :json } do
8
8
  namespace :cli do
9
9
  namespace :v1 do
10
- resource :webhooks, only: [] do
11
- post :docker_hub
12
- post :github
13
- post :azure
14
- post :amazon
15
- post :google
16
- end
17
-
18
10
  resources :projects, only: ['index', 'show', 'create', 'destroy'], param: :slug do
19
11
  scope module: :projects do
20
12
  resource :compose_file, only: ['show', 'create', 'destroy']
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UffizziCore
4
- VERSION = '0.4.1'
4
+ VERSION = '0.6.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uffizzi_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Thurman
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-08-09 00:00:00.000000000 Z
12
+ date: 2022-08-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aasm
@@ -754,11 +754,9 @@ files:
754
754
  - app/jobs/uffizzi_core/activity_item/docker/update_digest_job.rb
755
755
  - app/jobs/uffizzi_core/application_job.rb
756
756
  - app/jobs/uffizzi_core/config_file/apply_job.rb
757
- - app/jobs/uffizzi_core/credential/docker_hub/create_webhook_job.rb
758
757
  - app/jobs/uffizzi_core/deployment/create_credential_job.rb
759
758
  - app/jobs/uffizzi_core/deployment/create_credentials_job.rb
760
759
  - app/jobs/uffizzi_core/deployment/create_job.rb
761
- - app/jobs/uffizzi_core/deployment/create_webhooks_job.rb
762
760
  - app/jobs/uffizzi_core/deployment/delete_credential_job.rb
763
761
  - app/jobs/uffizzi_core/deployment/delete_job.rb
764
762
  - app/jobs/uffizzi_core/deployment/deploy_containers_job.rb
@@ -920,7 +918,7 @@ files:
920
918
  - app/services/uffizzi_core/compose_file/parsers/services/healthcheck_parser_service.rb
921
919
  - app/services/uffizzi_core/compose_file/parsers/services/image_parser_service.rb
922
920
  - app/services/uffizzi_core/compose_file/parsers/services/secrets_parser_service.rb
923
- - app/services/uffizzi_core/compose_file/parsers/services/volumes_service.rb
921
+ - app/services/uffizzi_core/compose_file/parsers/services/volumes_parser_service.rb
924
922
  - app/services/uffizzi_core/compose_file/parsers/services_parser_service.rb
925
923
  - app/services/uffizzi_core/compose_file/parsers/variables_parser_service.rb
926
924
  - app/services/uffizzi_core/compose_file/parsers/volumes_parser_service.rb
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class UffizziCore::Credential::DockerHub::CreateWebhookJob < UffizziCore::ApplicationJob
4
- sidekiq_options queue: :accounts, retry: 5
5
-
6
- def perform(credential_id, image, deployment_id = nil)
7
- if deployment_id.present?
8
- Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment_id} DockerHub CreateWebhooksJob")
9
- end
10
-
11
- credential = UffizziCore::Credential.find(credential_id)
12
-
13
- UffizziCore::DockerHubService.create_webhook(credential, image)
14
- end
15
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class UffizziCore::Deployment::CreateWebhooksJob < UffizziCore::ApplicationJob
4
- sidekiq_options queue: :deployments, retry: 5
5
-
6
- def perform(id)
7
- Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{id} CreateWebhooksJob")
8
-
9
- deployment = UffizziCore::Deployment.find(id)
10
-
11
- UffizziCore::DeploymentService.create_webhooks(deployment)
12
- end
13
- end