uffizzi_core 0.4.2 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c77ce0a84cac9c658e95ca303c65b90bed161bdd6c588c18a126dab2e58175ab
4
- data.tar.gz: b8c916fd4b1beaf5354d1e4dda147e38d5b4431e7ec0feea880cf36041a445d4
3
+ metadata.gz: 5db1e654767dd515b19f3848fe63f6f0c194ac998f705bc78327b10ccf0cb401
4
+ data.tar.gz: 1743714525d2fd0e149bd16c4c7f6ec8a08c56d51cbec9df7ff559491c0712e5
5
5
  SHA512:
6
- metadata.gz: 0f1d7cc3cea3c4fda4615d67d8606bd122997ed96260f595358d5942c58222a3c38814466d7c3c6b7005eeaec77cb81bc48436bcff1c73228c0b6354d2897c49
7
- data.tar.gz: f9ca337c51fffd381a7c53744e59e60917ecd541fafd63cd94b5a5618dd5f32eab8f70cc6fbe6c153765eb47dbb9011875b5a45efd5882794fc5d6616e10076c
6
+ metadata.gz: 1df17b138ba1bfca50b4b30d68012c80d2d38cf1a4ff0374d3a10ebd711eb6aa8e2aa0d62dc5652c9178ea83b7543981332d023a22e45aa409b3dbeed40c7314
7
+ data.tar.gz: 36da046695524c28f9f8d9c101d906ef32018f79f9240ce2ee10c71a7d12a7b19dbe7d182a0a867ce47d27119074a665dbb5cefd9259092325dbd1b5a323e7e2
@@ -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
@@ -5,9 +5,8 @@ class UffizziCore::ComposeFile::Parsers::Services::ImageParserService
5
5
  def parse(value)
6
6
  return {} if value.blank?
7
7
 
8
- parse_error = UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_image_value', value: value)
9
- image_path, tag = get_image_path_and_tag(value, parse_error)
10
- raise parse_error if image_path.blank?
8
+ image_path, tag = get_image_path_and_tag(value)
9
+ raise_parse_error(value) if image_path.blank?
11
10
 
12
11
  tag = Settings.compose.default_tag if tag.blank?
13
12
 
@@ -27,7 +26,11 @@ class UffizziCore::ComposeFile::Parsers::Services::ImageParserService
27
26
 
28
27
  private
29
28
 
30
- def get_image_path_and_tag(value, parse_error)
29
+ def raise_parse_error(value)
30
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_image_value', value: value)
31
+ end
32
+
33
+ def get_image_path_and_tag(value)
31
34
  image_path_parts = value.downcase.split(':')
32
35
  case image_path_parts.size
33
36
  when 1
@@ -40,18 +43,18 @@ class UffizziCore::ComposeFile::Parsers::Services::ImageParserService
40
43
  elsif tag_pattern.match?(value)
41
44
  [image_path_parts[0], image_path_parts[1]]
42
45
  else
43
- raise parse_error
46
+ raise_parse_error(value)
44
47
  end
45
48
  when 3
46
49
  ["#{image_path_parts[0]}:#{image_path_parts[1]}", image_path_parts[2]]
47
50
  else
48
- raise parse_error
51
+ raise_parse_error(value)
49
52
  end
50
53
  end
51
54
 
52
55
  def url?(image_path)
53
56
  uri = URI(add_https_if_needed(image_path))
54
- uri.host.present? && uri.host =~ /\w+\.(\w+\.)*\w+/ && uri.path.present?
57
+ uri.host.present? && uri.host =~ /(localhost(:\d+)?|\w+\.(\w+\.)*\w+)/ && uri.path.present?
55
58
  rescue URI::InvalidURIError
56
59
  false
57
60
  end
@@ -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
@@ -208,28 +207,6 @@ class UffizziCore::DeploymentService
208
207
  deployment.save!
209
208
  end
210
209
 
211
- def create_webhooks(deployment)
212
- credential = deployment.project.account.credentials.docker_hub.active.first
213
-
214
- if !deployment.containers.with_docker_hub_repo.exists? || !credential.present?
215
- Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{deployment.id} create_webhooks no dockerhub containers or credential")
216
- return
217
- end
218
-
219
- accounts = UffizziCore::DockerHubService.accounts(credential)
220
-
221
- deployment.containers.with_docker_hub_repo.find_each do |container|
222
- if !accounts.include?(container.repo.namespace)
223
- logger_message = "DEPLOYMENT_PROCESS deployment_id=#{deployment.id} no namespace(#{container.repo.namespace})
224
- in accounts(#{accounts.inspect})"
225
- Rails.logger.info(logger_message)
226
- next
227
- end
228
-
229
- UffizziCore::Credential::DockerHub::CreateWebhookJob.perform_async(credential.id, container.image, deployment.id)
230
- end
231
- end
232
-
233
210
  def pull_request_payload_present?(deployment)
234
211
  deployment.continuous_preview_payload.present? && deployment.continuous_preview_payload['pull_request'].present?
235
212
  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.2'
4
+ VERSION = '0.6.1'
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.2
4
+ version: 0.6.1
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-10 00:00:00.000000000 Z
12
+ date: 2022-08-17 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