uffizzi_core 0.3.7 → 0.4.1

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: 9e55dc8e6b2d5e6cde4394b3f3f526a82e8a0ddc7d0d5ce6aa9e42ad407783c9
4
- data.tar.gz: 44fdd5695f09b5a93b146370ebc1076278bc55e89f22029e6f30d07cfa3f2d66
3
+ metadata.gz: 5a6a5a5e07ef682bca2fc1b22a628cc093282f720f3a98a18ee290d84a05c5fc
4
+ data.tar.gz: 50e1ec427b2cd4227f357b57707f9151edb2e2db62a77ee8e7f149652bd66e7d
5
5
  SHA512:
6
- metadata.gz: dbd3510abe5c9919eba8f9abcf35cf9b49b03952d84da0dbe43e12d5615544b1ceaea9125fc877694d56c878d8b372f5777912006c9b361800ed3b02543a5be1
7
- data.tar.gz: 2b37022d8b1d9142ab4d9977bbe1adc5e03ae7d5492a60a27df2975ba0d0c7808a3f6c1b7542a8bd4f8f7214a6b753a25036bbbbd836b7dc94a59262aaaa9779
6
+ metadata.gz: 0b69cbdcaa12a2800e80f5f6784813333b88fffbc1df28792cbc1c07ed78a6ba6e4ee0d7a38372db46ae99d22be36d2ef88377158145829ea2153901542f5f00
7
+ data.tar.gz: e121d4b060d698b4d3e07256ce7a977ec14d62a727ebd5ed9127b66d4a74ee4d29bf22327084573ae13b6d25d9a2111276610644d1f61fbb078dfa4ef01859a3
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::DockerRegistryClient::RequestResult < Hashie::Mash
4
+ disable_warnings :key
5
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::DockerRegistryClient
4
+ def initialize(credentials)
5
+ @registry_url = credentials.registry_url
6
+ @connection = build_connection(@registry_url, credentials.username, credentials.password)
7
+ end
8
+
9
+ def authenticated?
10
+ response = @connection.head('/v2/')
11
+ response.status == 200
12
+ end
13
+
14
+ private
15
+
16
+ def build_connection(registry_url, username, password)
17
+ Faraday.new(registry_url) do |conn|
18
+ conn.request(:basic_auth, username, password)
19
+ conn.request(:json)
20
+ conn.response(:json)
21
+ conn.adapter(Faraday.default_adapter)
22
+ end
23
+ end
24
+ end
@@ -15,7 +15,6 @@ class UffizziCore::Api::Cli::V1::Account::CredentialsController < UffizziCore::A
15
15
  render json: { credentials: credentials }, status: :ok
16
16
  end
17
17
 
18
- # rubocop:disable Layout/LineLength
19
18
  # Create account credential
20
19
  #
21
20
  # @path [POST] /api/cli/v1/account/credentials
@@ -25,8 +24,9 @@ class UffizziCore::Api::Cli::V1::Account::CredentialsController < UffizziCore::A
25
24
  # @response [object<errors>] 422 Unprocessable entity
26
25
  #
27
26
  # @example
28
- # type can be one of UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry
29
- # rubocop:enable Layout/LineLength
27
+ # Possible types:
28
+ # UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub,
29
+ # UffizziCore::Credential::DockerRegistry, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry
30
30
  def create
31
31
  credential_form = UffizziCore::Api::Cli::V1::Account::Credential::CreateForm.new
32
32
  credential_form.assign_attributes(credential_params)
@@ -37,6 +37,10 @@ module UffizziCore::Concerns::Models::Credential
37
37
  type == UffizziCore::Credential::DockerHub.name
38
38
  end
39
39
 
40
+ def docker_registry?
41
+ type == UffizziCore::Credential::DockerRegistry.name
42
+ end
43
+
40
44
  def azure?
41
45
  type == UffizziCore::Credential::Azure.name
42
46
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Credential::DockerRegistry < UffizziCore::Credential
4
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Repo::DockerRegistry < UffizziCore::Repo
4
+ end
@@ -6,6 +6,7 @@ module UffizziCore::CredentialRepo
6
6
  included do
7
7
  scope :by_type, ->(type) { where(type: type) }
8
8
  scope :docker_hub, -> { by_type(UffizziCore::Credential::DockerHub.name) }
9
+ scope :docker_registry, -> { by_type(UffizziCore::Credential::DockerRegistry.name) }
9
10
  scope :azure, -> { by_type(UffizziCore::Credential::Azure.name) }
10
11
  scope :google, -> { by_type(UffizziCore::Credential::Google.name) }
11
12
  scope :amazon, -> { by_type(UffizziCore::Credential::Amazon.name) }
@@ -13,6 +14,7 @@ module UffizziCore::CredentialRepo
13
14
  scope :deployable, -> {
14
15
  by_type([
15
16
  UffizziCore::Credential::DockerHub.name,
17
+ UffizziCore::Credential::DockerRegistry.name,
16
18
  UffizziCore::Credential::Azure.name,
17
19
  UffizziCore::Credential::Google.name,
18
20
  UffizziCore::Credential::Amazon.name,
@@ -24,13 +24,16 @@ class UffizziCore::Controller::DeployContainers::ContainerSerializer < UffizziCo
24
24
 
25
25
  def image
26
26
  repo = object.repo
27
- credential = UffizziCore::RepoService.credential(repo)
28
-
29
27
  case repo.type
30
- when UffizziCore::Repo::Google.name, UffizziCore::Repo::Amazon.name, UffizziCore::Repo::Azure.name,
31
- UffizziCore::Repo::GithubContainerRegistry.name
32
- registry_host = URI.parse(credential.registry_url).host
28
+ when
29
+ UffizziCore::Repo::Google.name,
30
+ UffizziCore::Repo::Amazon.name,
31
+ UffizziCore::Repo::Azure.name,
32
+ UffizziCore::Repo::GithubContainerRegistry.name,
33
+ UffizziCore::Repo::DockerRegistry.name
33
34
 
35
+ credential = UffizziCore::RepoService.credential(repo)
36
+ registry_host = URI.parse(credential.registry_url).host
34
37
  "#{registry_host}/#{object.image}"
35
38
  else
36
39
  object.image
@@ -107,7 +107,8 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
107
107
  def image_name(container_data, image_data)
108
108
  if image_data[:registry_url].present? &&
109
109
  !UffizziCore::ComposeFile::ContainerService.google?(container_data) &&
110
- !UffizziCore::ComposeFile::ContainerService.github_container_registry?(container_data)
110
+ !UffizziCore::ComposeFile::ContainerService.github_container_registry?(container_data) &&
111
+ !UffizziCore::ComposeFile::ContainerService.docker_registry?(container_data)
111
112
  image_data[:name]
112
113
  else
113
114
  "#{image_data[:namespace]}/#{image_data[:name]}"
@@ -166,6 +167,8 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
166
167
  case repo_type
167
168
  when UffizziCore::Repo::DockerHub.name
168
169
  build_docker_repo_attributes(image_data, credentials, :docker_hub, UffizziCore::Repo::DockerHub.name)
170
+ when UffizziCore::Repo::DockerRegistry.name
171
+ build_docker_repo_attributes(image_data, credentials, :docker_registry, UffizziCore::Repo::DockerRegistry.name)
169
172
  when UffizziCore::Repo::Azure.name
170
173
  build_docker_repo_attributes(image_data, credentials, :azure, UffizziCore::Repo::Azure.name)
171
174
  when UffizziCore::Repo::Google.name
@@ -184,6 +187,8 @@ class UffizziCore::ComposeFile::Builders::ContainerBuilderService
184
187
  UffizziCore::Repo::Azure.name
185
188
  elsif UffizziCore::ComposeFile::ContainerService.docker_hub?(container_data)
186
189
  UffizziCore::Repo::DockerHub.name
190
+ elsif UffizziCore::ComposeFile::ContainerService.docker_registry?(container_data)
191
+ UffizziCore::Repo::DockerRegistry.name
187
192
  elsif UffizziCore::ComposeFile::ContainerService.google?(container_data)
188
193
  UffizziCore::Repo::Google.name
189
194
  elsif UffizziCore::ComposeFile::ContainerService.github_container_registry?(container_data)
@@ -27,6 +27,15 @@ class UffizziCore::ComposeFile::ContainerService
27
27
  registry_url.nil? && repository_url.nil?
28
28
  end
29
29
 
30
+ def docker_registry?(container)
31
+ registry_url = container.dig(:image, :registry_url)
32
+ registry_domain_regexp = /(\w+\.\w{2,})(?::\d+)?\z/
33
+ registry_domain = registry_url.match(registry_domain_regexp)&.to_a&.last
34
+ return false if registry_url.nil? || registry_domain.nil?
35
+
36
+ ['amazonaws.com', 'azurecr.io', 'gcr.io', 'ghcr.io'].exclude?(registry_domain)
37
+ end
38
+
30
39
  def github_container_registry?(container)
31
40
  registry_url = container.dig(:image, :registry_url)
32
41
 
@@ -2,24 +2,23 @@
2
2
 
3
3
  class UffizziCore::ComposeFile::Parsers::Services::ImageParserService
4
4
  class << self
5
- def parse(image)
6
- return {} if image.blank?
5
+ def parse(value)
6
+ return {} if value.blank?
7
7
 
8
- image_parts = image.downcase.split(':')
9
- raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_image_value', value: image) if image_parts.count > 2
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?
10
11
 
11
- image_name, tag = image_parts
12
12
  tag = Settings.compose.default_tag if tag.blank?
13
- raise UffizziCore::ComposeFile::ParseError, I18n.t('compose.invalid_image_value', value: image) if image_name.blank?
14
13
 
15
- if valid_image_url?(image_name)
16
- url, namespace, name = parse_image_url(image_name)
14
+ if url?(image_path)
15
+ host, namespace, name = parse_image_url(image_path)
17
16
  else
18
- namespace, name = parse_docker_hub_image(image_name)
17
+ namespace, name = parse_docker_hub_image(image_path)
19
18
  end
20
19
 
21
20
  {
22
- registry_url: url,
21
+ registry_url: host,
23
22
  namespace: namespace,
24
23
  name: name,
25
24
  tag: tag,
@@ -28,44 +27,55 @@ class UffizziCore::ComposeFile::Parsers::Services::ImageParserService
28
27
 
29
28
  private
30
29
 
31
- def valid_image_url?(image_name)
32
- url = prepare_image_url(image_name)
33
-
34
- URI(url).host.present? && URI(url).host =~ /\w+\.\w+/ && URI(url).path.present?
35
- end
36
-
37
- def prepare_image_url(image_name)
38
- if image_name.start_with?('https://')
39
- image_name
30
+ def get_image_path_and_tag(value, parse_error)
31
+ image_path_parts = value.downcase.split(':')
32
+ case image_path_parts.size
33
+ when 1
34
+ image_path_parts[0]
35
+ when 2
36
+ uri_pattern = /\A\w[\w.-]+:\d+\//
37
+ tag_pattern = /:\w[\w.-]*\z/
38
+ if uri_pattern.match?(value)
39
+ "#{image_path_parts[0]}:#{image_path_parts[1]}"
40
+ elsif tag_pattern.match?(value)
41
+ [image_path_parts[0], image_path_parts[1]]
42
+ else
43
+ raise parse_error
44
+ end
45
+ when 3
46
+ ["#{image_path_parts[0]}:#{image_path_parts[1]}", image_path_parts[2]]
40
47
  else
41
- "https://#{image_name}"
48
+ raise parse_error
42
49
  end
43
50
  end
44
51
 
45
- def parse_image_url(image_name)
46
- prepared_url = prepare_image_url(image_name)
47
-
48
- uri = URI(prepared_url)
49
- url = uri.host
50
- path = uri.path.delete_suffix('/').delete_prefix('/')
52
+ def url?(image_path)
53
+ uri = URI(add_https_if_needed(image_path))
54
+ uri.host.present? && uri.host =~ /\w+\.(\w+\.)*\w+/ && uri.path.present?
55
+ rescue URI::InvalidURIError
56
+ false
57
+ end
51
58
 
52
- namespace, name = parse_image_path(path)
59
+ def add_https_if_needed(image_path)
60
+ image_path.start_with?('https://') ? image_path : "https://#{image_path}"
61
+ end
53
62
 
54
- [url, namespace, name]
63
+ def parse_image_url(image_path)
64
+ uri = URI(add_https_if_needed(image_path))
65
+ host = "#{uri.host}:#{uri.port}"
66
+ path = uri.path.delete_prefix('/')
67
+ namespace, name = get_namespace_and_name(path)
68
+ [host, namespace, name]
55
69
  end
56
70
 
57
- def parse_image_path(path)
58
- path_parts = path.split('/', 2)
71
+ def get_namespace_and_name(path)
72
+ path_parts = path.rpartition('/')
59
73
 
60
- if path_parts.count == 1
61
- namespace = nil
62
- name = path_parts.first
74
+ if path_parts.first.empty?
75
+ [nil, path_parts.last]
63
76
  else
64
- namespace = path_parts.first
65
- name = path_parts.last
77
+ [path_parts.first, path_parts.last]
66
78
  end
67
-
68
- [namespace, name]
69
79
  end
70
80
 
71
81
  def parse_docker_hub_image(image_name)
@@ -66,12 +66,28 @@ class UffizziCore::ComposeFileService
66
66
  end
67
67
 
68
68
  def update_secret!(compose_file, secret)
69
- compose_file.template.payload['containers_attributes'].map do |container|
70
- if UffizziCore::ComposeFile::ContainerService.has_secret?(container, secret)
71
- container = UffizziCore::ComposeFile::ContainerService.update_secret(container, secret)
69
+ compose_file.template.payload['containers_attributes'].each do |container|
70
+ next unless UffizziCore::ComposeFile::ContainerService.has_secret?(container, secret)
71
+
72
+ UffizziCore::ComposeFile::ContainerService.update_secret(container, secret)
73
+ next if compose_file.payload['errors'].blank?
74
+
75
+ compose_file_errors = compose_file.payload['errors'].presence
76
+ secrets_errors = compose_file_errors[UffizziCore::ComposeFile::ErrorsService::SECRETS_ERROR_KEY].presence
77
+ new_secrets_errors = secrets_errors.reject { |secret_errors| secret_errors.include?(secret.name) }
78
+
79
+ if new_secrets_errors.present?
80
+ new_errors = { UffizziCore::ComposeFile::ErrorsService::SECRETS_ERROR_KEY => new_secrets_errors }
81
+ UffizziCore::ComposeFile::ErrorsService.update_compose_errors!(compose_file,
82
+ compose_file_errors.merge(new_errors),
83
+ compose_file.content)
84
+ next
72
85
  end
73
86
 
74
- container
87
+ compose_file_errors.delete(['secret_variables'])
88
+ next UffizziCore::ComposeFile::ErrorsService.reset_compose_errors!(compose_file) if compose_file_errors.empty?
89
+
90
+ UffizziCore::ComposeFile::ErrorsService.update_compose_errors!(compose_file, compose_file_errors, compose_file.content)
75
91
  end
76
92
 
77
93
  compose_file.template.save!
@@ -6,6 +6,8 @@ class UffizziCore::CredentialService
6
6
  status = case credential.type
7
7
  when UffizziCore::Credential::DockerHub.name
8
8
  UffizziCore::DockerHub::CredentialService.credential_correct?(credential)
9
+ when UffizziCore::Credential::DockerRegistry.name
10
+ UffizziCore::DockerRegistry::CredentialService.credential_correct?(credential)
9
11
  when UffizziCore::Credential::GithubContainerRegistry.name
10
12
  UffizziCore::GithubContainerRegistry::CredentialService.credential_correct?(credential)
11
13
  when UffizziCore::Credential::Azure.name
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::DockerRegistry::CredentialService
4
+ class << self
5
+ def credential_correct?(credential)
6
+ client(credential).authenticated?
7
+ end
8
+
9
+ private
10
+
11
+ def client(credential)
12
+ UffizziCore::DockerRegistryClient.new(credential)
13
+ end
14
+ end
15
+ end
@@ -27,10 +27,11 @@ class UffizziCore::ProjectService
27
27
  return unless UffizziCore::ComposeFileService.has_secret?(compose_file, secret)
28
28
 
29
29
  error_message = I18n.t('compose.project_secret_not_found', secret: secret['name'])
30
- error = { UffizziCore::ComposeFile::ErrorsService::SECRETS_ERROR_KEY => [error_message] }
31
-
32
- existing_errors = compose_file.payload['errors'].presence || {}
33
- new_errors = existing_errors.merge(error)
30
+ compose_file_errors = compose_file.payload['errors'] || {}
31
+ secrets_errors = compose_file_errors[UffizziCore::ComposeFile::ErrorsService::SECRETS_ERROR_KEY].presence || []
32
+ new_secrets_errors = secrets_errors.append(error_message).uniq
33
+ error = { UffizziCore::ComposeFile::ErrorsService::SECRETS_ERROR_KEY => new_secrets_errors }
34
+ new_errors = compose_file_errors.merge(error)
34
35
 
35
36
  UffizziCore::ComposeFile::ErrorsService.update_compose_errors!(compose_file, new_errors, compose_file.content)
36
37
  end
@@ -16,6 +16,8 @@ class UffizziCore::RepoService
16
16
  credentials.github_container_registry.first
17
17
  when UffizziCore::Repo::DockerHub.name
18
18
  credentials.docker_hub.first
19
+ when UffizziCore::Repo::DockerRegistry.name
20
+ credentials.docker_registry.first
19
21
  when UffizziCore::Repo::Azure.name
20
22
  credentials.azure.first
21
23
  when UffizziCore::Repo::Google.name
@@ -21,3 +21,7 @@ en:
21
21
  containers:
22
22
  max_memory_limit_error: 'Memory limit of containers must be less than %{max}'
23
23
  max_memory_request_error: 'Memory request of containers must be less than %{max}'
24
+ uffizzi_core/user:
25
+ attributes:
26
+ email:
27
+ invalid: Invalid email address
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UffizziCore
4
- VERSION = '0.3.7'
4
+ VERSION = '0.4.1'
5
5
  end
@@ -80,7 +80,7 @@
80
80
  "default": {
81
81
  "description": "Create account credential",
82
82
  "examples": {
83
- "application/json": "type can be one of UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry"
83
+ "application/json": "Possible types:\nUffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub,\nUffizziCore::Credential::DockerRegistry, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry"
84
84
  }
85
85
  },
86
86
  "201": {
@@ -1301,16 +1301,8 @@
1301
1301
  }
1302
1302
  ],
1303
1303
  "responses": {
1304
- "200": {
1305
- "description": "OK",
1306
- "schema": {
1307
- "type": "object",
1308
- "properties": {
1309
- "project": {
1310
- "$ref": "#/definitions/Project"
1311
- }
1312
- }
1313
- }
1304
+ "204": {
1305
+ "description": "No content"
1314
1306
  },
1315
1307
  "404": {
1316
1308
  "description": "Not Found"
@@ -1339,8 +1331,16 @@
1339
1331
  }
1340
1332
  ],
1341
1333
  "responses": {
1342
- "204": {
1343
- "description": "No content"
1334
+ "200": {
1335
+ "description": "OK",
1336
+ "schema": {
1337
+ "type": "object",
1338
+ "properties": {
1339
+ "project": {
1340
+ "$ref": "#/definitions/Project"
1341
+ }
1342
+ }
1343
+ }
1344
1344
  },
1345
1345
  "404": {
1346
1346
  "description": "Not Found"
@@ -1720,4 +1720,4 @@
1720
1720
  }
1721
1721
  }
1722
1722
  }
1723
- }
1723
+ }
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.3.7
4
+ version: 0.4.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-02 00:00:00.000000000 Z
12
+ date: 2022-08-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aasm
@@ -695,6 +695,8 @@ files:
695
695
  - app/clients/uffizzi_core/docker_hub_client.rb
696
696
  - app/clients/uffizzi_core/docker_hub_client/not_authorized_error.rb
697
697
  - app/clients/uffizzi_core/docker_hub_client/request_result.rb
698
+ - app/clients/uffizzi_core/docker_registry_client.rb
699
+ - app/clients/uffizzi_core/docker_registry_client/request_result.rb
698
700
  - app/clients/uffizzi_core/github_container_registry_client.rb
699
701
  - app/clients/uffizzi_core/github_container_registry_client/request_result.rb
700
702
  - app/clients/uffizzi_core/google_registry_client.rb
@@ -809,6 +811,7 @@ files:
809
811
  - app/models/uffizzi_core/credential/amazon.rb
810
812
  - app/models/uffizzi_core/credential/azure.rb
811
813
  - app/models/uffizzi_core/credential/docker_hub.rb
814
+ - app/models/uffizzi_core/credential/docker_registry.rb
812
815
  - app/models/uffizzi_core/credential/github.rb
813
816
  - app/models/uffizzi_core/credential/github_container_registry.rb
814
817
  - app/models/uffizzi_core/credential/google.rb
@@ -827,6 +830,7 @@ files:
827
830
  - app/models/uffizzi_core/repo/amazon.rb
828
831
  - app/models/uffizzi_core/repo/azure.rb
829
832
  - app/models/uffizzi_core/repo/docker_hub.rb
833
+ - app/models/uffizzi_core/repo/docker_registry.rb
830
834
  - app/models/uffizzi_core/repo/github.rb
831
835
  - app/models/uffizzi_core/repo/github_container_registry.rb
832
836
  - app/models/uffizzi_core/repo/google.rb
@@ -928,6 +932,7 @@ files:
928
932
  - app/services/uffizzi_core/deployment_service.rb
929
933
  - app/services/uffizzi_core/docker_hub/credential_service.rb
930
934
  - app/services/uffizzi_core/docker_hub_service.rb
935
+ - app/services/uffizzi_core/docker_registry/credential_service.rb
931
936
  - app/services/uffizzi_core/github_container_registry/credential_service.rb
932
937
  - app/services/uffizzi_core/google/credential_service.rb
933
938
  - app/services/uffizzi_core/google_service.rb