uffizzi_core 0.2.5 → 0.3.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: 3cf0385b90270853a0014af3854253436050a54617c7351acb13bd93ab166e92
4
- data.tar.gz: 0b0cc859f136ae6c78c358668db24b894e860f4dd56d1b2916aa90af1e3deed0
3
+ metadata.gz: 40e3a8b7bbd915a1e861fbc7cff0b188741c49be50aa02d8ee05e790e47038a3
4
+ data.tar.gz: d02d1b3eba6bfbc471266dd28f23200a43cc88c71e7139c1d5bf43c915292f31
5
5
  SHA512:
6
- metadata.gz: 6a9b81839c201a3958e45ff607141ac0b871694e56d8ffdc9b7c0a94963b9ad620b7d3a7e631b9d346ec8944d8d2e2ce8f3df2ed23faae6c7c637ce4866217b9
7
- data.tar.gz: 0eea7cb1e2e2c01d80f6b01733db2424bce357d1f6da54b3f5c118dc78b649db8ac54ff499ca315f3642de55f3d906177b705aa372ae682bc87f6c26a2ac5910
6
+ metadata.gz: 4bff4a31ff842379bd0907336285b65f31052b58d55ecd104632d483304e07722b75d2591eba942c6583e99ea4f3d23c08765a42b1b102e478f031b5001c4ff4
7
+ data.tar.gz: 7d0e59f416be2ed26806363b85efa090eb3b02a3ba34546cd5103467d0f80566ddcb7f34270250c8112e65e2c2e35f2ce8dd6fab22f7fb958207e89f72c749f9
@@ -19,6 +19,7 @@ class UffizziCore::Api::Cli::V1::Deployment::CreateForm < UffizziCore::Deploymen
19
19
  { healthcheck: {} },
20
20
  { variables: [:name, :value],
21
21
  secret_variables: [:name, :value],
22
+ volumes: [:source, :target, :type, :read_only],
22
23
  repo_attributes: [
23
24
  :namespace,
24
25
  :name,
@@ -17,6 +17,7 @@ class UffizziCore::Api::Cli::V1::Deployment::UpdateForm < UffizziCore::Deploymen
17
17
  :continuously_deploy,
18
18
  { variables: [:name, :value],
19
19
  secret_variables: [:name, :value],
20
+ volumes: [:source, :target, :type, :read_only],
20
21
  repo_attributes: [
21
22
  :namespace,
22
23
  :name,
@@ -21,6 +21,7 @@ class UffizziCore::Api::Cli::V1::Template::CreateForm < UffizziCore::Template
21
21
  :healthcheck,
22
22
  { variables: [:name, :value],
23
23
  secret_variables: [:name, :value],
24
+ volumes: [:source, :target, :type, :read_only],
24
25
  repo_attributes: [
25
26
  :namespace,
26
27
  :name,
@@ -20,7 +20,8 @@ class UffizziCore::Api::Cli::V1::Projects::DeploymentSerializer::ContainerSerial
20
20
  :repo_id,
21
21
  :continuously_deploy,
22
22
  :receive_incoming_requests,
23
- :healthcheck
23
+ :healthcheck,
24
+ :volumes
24
25
 
25
26
  def secret_variables
26
27
  return unless object.secret_variables.present?
@@ -16,7 +16,9 @@ class UffizziCore::Controller::DeployContainers::ContainerSerializer < UffizziCo
16
16
  :public,
17
17
  :controller_name,
18
18
  :receive_incoming_requests,
19
- :healthcheck
19
+ :healthcheck,
20
+ :volumes,
21
+ :service_name
20
22
 
21
23
  has_many :container_config_files
22
24
 
@@ -16,20 +16,6 @@ class UffizziCore::AmazonService
16
16
  parsed_host[3]
17
17
  end
18
18
 
19
- def process_webhook(event, event_data)
20
- case event
21
- when 'PUSH'
22
- source = UffizziCore::Repo::Amazon.name
23
- image = event_data['repository-name']
24
- tag = event_data['image-tag']
25
-
26
- UffizziCore::ContinuouslyDeployService.run_docker_registry_process(source, image, tag)
27
- UffizziCore::ContinuousPreviewService.run_docker_registry_process(source, image, tag)
28
- else
29
- Rails.logger.warn("Amazon #{event} event doesn't support")
30
- end
31
- end
32
-
33
19
  private
34
20
 
35
21
  def client(credential)
@@ -9,6 +9,7 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
9
9
  @repositories = repositories
10
10
  end
11
11
 
12
+ # rubocop:disable Metrics/PerceivedComplexity
12
13
  def build_attributes(container_data, ingress_data, continuous_preview_global_data, compose_dependencies)
13
14
  image_data = container_data[:image] || {}
14
15
  build_data = container_data[:build] || {}
@@ -18,6 +19,7 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
18
19
  secrets = container_data[:secrets] || []
19
20
  container_name = container_data[:container_name]
20
21
  healthcheck_data = container_data[:healthcheck] || {}
22
+ volumes = container_data[:volumes] || []
21
23
 
22
24
  env_file_dependencies = UffizziCore::ComposeFile::GithubDependenciesService.env_file_dependencies_for_container(compose_dependencies,
23
25
  container_name)
@@ -44,8 +46,10 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
44
46
  service_name: container_name,
45
47
  name: container_name,
46
48
  healthcheck: healthcheck_data,
49
+ volumes: volumes,
47
50
  }
48
51
  end
52
+ # rubocop:enable Metrics/PerceivedComplexity
49
53
 
50
54
  private
51
55
 
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Parsers::Services::VolumesService
4
+ HOST_VOLUME_TYPE = :host
5
+ NAMED_VOLUME_TYPE = :named
6
+ ANONYMOUS_VOLUME_TYPE = :anonymous
7
+ READONLY_OPTION = 'ro'
8
+ READ_WRITE_OPTION = 'rw'
9
+
10
+ class << self
11
+ def parse(volumes, named_volumes_names, service_name)
12
+ return [] if volumes.blank?
13
+
14
+ volumes.map do |volume|
15
+ volume_data = case volume
16
+ when String
17
+ process_short_syntax(volume, named_volumes_names, service_name)
18
+ when Hash
19
+ process_long_syntax(volume, named_volumes_names, service_name)
20
+ else
21
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :volumes)
22
+ end
23
+
24
+ volume_data
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def process_short_syntax(volume_data, named_volumes_names, service_name)
31
+ volume_parts = volume_data.split(':').map(&:strip)
32
+ has_read_only = volume_parts.include?(READONLY_OPTION)
33
+ part1, part2 = volume_parts
34
+ source_path = part1
35
+ target_path = [READONLY_OPTION, READ_WRITE_OPTION].include?(part2.to_s.downcase) ? nil : part2
36
+
37
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_prop_is_required', prop_name: 'source') if source_path.blank?
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
+ }
49
+ end
50
+
51
+ def process_long_syntax(volume_data, named_volumes_names, service_name)
52
+ source_path = volume_data['source'].to_s.strip
53
+ target_path = volume_data['target'].to_s.strip
54
+ has_read_only = volume_data['read_only'].present?
55
+
56
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_prop_is_required', prop_name: 'source') if source_path.blank?
57
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_prop_is_required', prop_name: 'target') if target_path.blank?
58
+
59
+ volume_type = volume_type(source_path, target_path)
60
+
61
+ check_named_volume_existence(source_path, target_path, named_volumes_names, service_name) if volume_type == NAMED_VOLUME_TYPE
62
+
63
+ {
64
+ source: source_path,
65
+ target: target_path,
66
+ type: volume_type,
67
+ read_only: has_read_only,
68
+ }
69
+ end
70
+
71
+ def volume_type(source_path, target_path)
72
+ if path?(source_path) && path?(target_path)
73
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_type_not_supported', type: HOST_VOLUME_TYPE)
74
+ end
75
+
76
+ return ANONYMOUS_VOLUME_TYPE if path?(source_path) && target_path.blank?
77
+ return NAMED_VOLUME_TYPE if source_path.present? && !path?(source_path) && path?(target_path)
78
+
79
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.volume_path_is_invalid', path: [source_path, target_path].join(':'))
80
+ end
81
+
82
+ def path?(path)
83
+ /^(\/|\.\/|~\/)/.match?(path)
84
+ end
85
+
86
+ def check_named_volume_existence(source_path, target_path, named_volumes_names, service_name)
87
+ return if named_volumes_names.include?(source_path)
88
+
89
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.named_volume_not_exists', source_path: source_path,
90
+ target_path: target_path,
91
+ service_name: service_name)
92
+ end
93
+ end
94
+ end
@@ -4,11 +4,12 @@ class UffizziCore::ComposeFile::Parsers::ServicesParserService
4
4
  class << self
5
5
  include UffizziCore::DependencyInjectionConcern
6
6
 
7
- def parse(services, global_configs_data, global_secrets_data, compose_payload)
7
+ def parse(services, global_configs_data, global_secrets_data, compose_payload, global_named_volume_names)
8
8
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.no_services') if services.nil? || services.keys.empty?
9
9
 
10
10
  services.keys.map do |service|
11
- service_data = prepare_service_data(service, services.fetch(service), global_configs_data, global_secrets_data, compose_payload)
11
+ service_data = prepare_service_data(service, services.fetch(service), global_configs_data,
12
+ global_secrets_data, compose_payload, global_named_volume_names)
12
13
 
13
14
  if service_data[:image].blank? && service_data[:build].blank?
14
15
  raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.image_build_no_specified', value: service)
@@ -20,7 +21,8 @@ class UffizziCore::ComposeFile::Parsers::ServicesParserService
20
21
 
21
22
  private
22
23
 
23
- def prepare_service_data(service_name, service_data, global_configs_data, global_secrets_data, compose_payload)
24
+ def prepare_service_data(service_name, service_data, global_configs_data,
25
+ global_secrets_data, compose_payload, global_named_volume_names)
24
26
  options_data = {}
25
27
  service_data.each_pair do |key, value|
26
28
  service_key = key.to_sym
@@ -48,6 +50,10 @@ class UffizziCore::ComposeFile::Parsers::ServicesParserService
48
50
  UffizziCore::ComposeFile::Parsers::Services::HealthcheckParserService.parse(value)
49
51
  when :'x-uffizzi-continuous-preview', :'x-uffizzi-continuous-previews'
50
52
  UffizziCore::ComposeFile::Parsers::ContinuousPreviewParserService.parse(value)
53
+ when :volumes
54
+ UffizziCore::ComposeFile::Parsers::Services::VolumesService.parse(value,
55
+ global_named_volume_names,
56
+ service_name)
51
57
  end
52
58
  end
53
59
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ComposeFile::Parsers::VolumesParserService
4
+ VALID_VOLUME_NAME_REGEX = /^[a-zA-Z0-9._-]+$/.freeze
5
+
6
+ class << self
7
+ def parse(volumes_data)
8
+ return [] if volumes_data.nil?
9
+
10
+ raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_type', option: :volumes) unless volumes_data.is_a?(Hash)
11
+
12
+ volume_names = volumes_data.keys
13
+ volume_names.each do |volume_name|
14
+ unless volume_name.match?(VALID_VOLUME_NAME_REGEX)
15
+ raise UffizziCore::ComposeFile::ParseError,
16
+ I18n.t('compose.volume_invalid_name', name: volume_name)
17
+ end
18
+ end
19
+
20
+ volume_names
21
+ end
22
+ end
23
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::ComposeFileService
3
+ class UffizziCore::ComposeFileService
4
4
  class << self
5
5
  def create(params, kind)
6
6
  compose_file_form = create_compose_form(params, kind)
@@ -19,13 +19,14 @@ module UffizziCore::ComposeFileService
19
19
  check_config_options_format(compose_data)
20
20
  configs_data = UffizziCore::ComposeFile::Parsers::ConfigsParserService.parse(compose_data['configs'])
21
21
  secrets_data = UffizziCore::ComposeFile::Parsers::SecretsParserService.parse(compose_data['secrets'])
22
+ named_volume_names = UffizziCore::ComposeFile::Parsers::VolumesParserService.parse(compose_data['volumes'])
22
23
  containers_data = UffizziCore::ComposeFile::Parsers::ServicesParserService.parse(
23
24
  compose_data['services'],
24
25
  configs_data,
25
26
  secrets_data,
26
27
  compose_payload,
28
+ named_volume_names,
27
29
  )
28
-
29
30
  continuous_preview_option = UffizziCore::ComposeFile::ConfigOptionService.continuous_preview_option(compose_data)
30
31
  continuous_preview_data = UffizziCore::ComposeFile::Parsers::ContinuousPreviewParserService.parse(continuous_preview_option)
31
32
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::ControllerService
3
+ class UffizziCore::ControllerService
4
4
  class << self
5
5
  def apply_config_file(deployment, config_file)
6
6
  body = {
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::CredentialService
3
+ class UffizziCore::CredentialService
4
4
  class << self
5
5
  def correct_credentials?(credential)
6
6
  status = case credential.type
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::DeploymentService
3
+ class UffizziCore::DeploymentService
4
4
  MIN_TARGET_PORT_RANGE = 37_000
5
5
  MAX_TARGET_PORT_RANGE = 39_999
6
6
 
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::DockerHubService
3
+ class UffizziCore::DockerHubService
4
4
  HOOK_NAME = 'Uffizzi OpenSource Deploy Webhook'
5
5
  HOOK_URL = "#{Settings.app.host}api/cli/v1/webhooks/docker_hub"
6
6
  REGISTRY = 'registry-1.docker.io'
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::GoogleService
4
+ class << self
5
+ def digest(credential, image, tag)
6
+ response = registry_client(credential).manifests(image: image, tag: tag)
7
+
8
+ response.headers['docker-content-digest']
9
+ end
10
+
11
+ private
12
+
13
+ def registry_client(credential)
14
+ UffizziCore::GoogleRegistryClient.new(
15
+ registry_url: credential.registry_url,
16
+ username: credential.username,
17
+ password: credential.password,
18
+ )
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::ProjectService
3
+ class UffizziCore::ProjectService
4
4
  class << self
5
5
  def update_compose_secrets(project)
6
6
  compose_file = project.compose_file
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module UffizziCore::RepoService
3
+ class UffizziCore::RepoService
4
4
  class << self
5
5
  def needs_target_port?(repo)
6
6
  return false if repo.nil?
@@ -61,6 +61,11 @@ en:
61
61
  string_or_array_error: "'%{option}' contains an invalid type, it should be a string, or an array"
62
62
  not_implemented: "'%{option}' option is not implemented"
63
63
  infinite_recursion: "Found infinite recursion for key '%{key}'"
64
+ volume_path_is_invalid: The path '%{path}' is invalid
65
+ volume_prop_is_required: The '%{prop_name}' is a required property
66
+ volume_invalid_name: "Volumes value '%{name}' does not match any of the regexes: '^[a-zA-Z0-9._-]+$'"
67
+ volume_type_not_supported: Volumes with type '%{type}' does not supported
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.
64
69
  secrets:
65
70
  duplicates_exists: Secret with key %{secrets} already exist.
66
71
  invalid_key_length: A secret key must be no longer than 256 characters.
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddVolumesToUffizziCoreContainers < ActiveRecord::Migration[6.1]
4
+ def change
5
+ add_column(:uffizzi_core_containers, :volumes, :jsonb)
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UffizziCore
4
- VERSION = '0.2.5'
4
+ VERSION = '0.3.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.2.5
4
+ version: 0.3.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-06-28 00:00:00.000000000 Z
12
+ date: 2022-07-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aasm
@@ -913,8 +913,10 @@ files:
913
913
  - app/services/uffizzi_core/compose_file/parsers/services/healthcheck_parser_service.rb
914
914
  - app/services/uffizzi_core/compose_file/parsers/services/image_parser_service.rb
915
915
  - app/services/uffizzi_core/compose_file/parsers/services/secrets_parser_service.rb
916
+ - app/services/uffizzi_core/compose_file/parsers/services/volumes_service.rb
916
917
  - app/services/uffizzi_core/compose_file/parsers/services_parser_service.rb
917
918
  - app/services/uffizzi_core/compose_file/parsers/variables_parser_service.rb
919
+ - app/services/uffizzi_core/compose_file/parsers/volumes_parser_service.rb
918
920
  - app/services/uffizzi_core/compose_file/template_service.rb
919
921
  - app/services/uffizzi_core/compose_file_service.rb
920
922
  - app/services/uffizzi_core/container_service.rb
@@ -925,6 +927,7 @@ files:
925
927
  - app/services/uffizzi_core/docker_hub_service.rb
926
928
  - app/services/uffizzi_core/github_container_registry/credential_service.rb
927
929
  - app/services/uffizzi_core/google/credential_service.rb
930
+ - app/services/uffizzi_core/google_service.rb
928
931
  - app/services/uffizzi_core/logs_service.rb
929
932
  - app/services/uffizzi_core/manage_activity_items_service.rb
930
933
  - app/services/uffizzi_core/project_service.rb
@@ -952,6 +955,7 @@ files:
952
955
  - db/migrate/20220329124542_add_resource_to_secrets.rb
953
956
  - db/migrate/20220329143241_remove_project_ref_from_secrets.rb
954
957
  - db/migrate/20220419074956_add_health_check_to_containers.rb
958
+ - db/migrate/20220422151523_add_volumes_to_uffizzi_core_containers.rb
955
959
  - db/migrate/20220525113412_rename_name_to_uffizzi_containers.rb
956
960
  - db/seeds.rb
957
961
  - lib/tasks/uffizzi_core_tasks.rake