uffizzi_core 0.1.3 → 0.1.5
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 +4 -4
- data/app/clients/uffizzi_core/docker_hub_client.rb +2 -0
- data/app/clients/uffizzi_core/github_container_registry_client/request_result.rb +7 -0
- data/app/clients/uffizzi_core/github_container_registry_client.rb +52 -0
- data/app/controllers/concerns/uffizzi_core/dependency_injection_concern.rb +2 -2
- data/app/controllers/uffizzi_core/api/cli/v1/account/credentials_controller.rb +45 -9
- data/app/controllers/uffizzi_core/api/cli/v1/projects/compose_files_controller.rb +2 -2
- data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments/application_controller.rb +1 -1
- data/app/controllers/uffizzi_core/api/cli/v1/projects/deployments_controller.rb +41 -10
- data/app/controllers/uffizzi_core/api/cli/v1/projects/secrets_controller.rb +18 -26
- data/app/controllers/uffizzi_core/application_controller.rb +9 -3
- data/app/errors/uffizzi_core/deployment/image_pull_error.rb +10 -0
- data/app/forms/uffizzi_core/api/cli/v1/account/credential/check_credential_form.rb +16 -0
- data/app/forms/uffizzi_core/api/cli/v1/account/credential/create_form.rb +1 -1
- data/app/forms/uffizzi_core/api/cli/v1/compose_file/check_credentials_form.rb +2 -2
- data/app/forms/uffizzi_core/api/cli/v1/compose_file/cli_form.rb +1 -18
- data/app/forms/uffizzi_core/api/cli/v1/compose_file/template_form.rb +1 -1
- data/app/forms/uffizzi_core/api/cli/v1/deployment/update_form.rb +90 -0
- data/app/forms/uffizzi_core/api/cli/v1/project/update_form.rb +1 -31
- data/app/forms/uffizzi_core/api/cli/v1/secret/bulk_assign_form.rb +39 -0
- data/app/jobs/uffizzi_core/deployment/manage_deploy_activity_item_job.rb +22 -3
- data/app/lib/uffizzi_core/rbac/user_access_service.rb +12 -14
- data/app/models/uffizzi_core/account.rb +1 -19
- data/app/models/uffizzi_core/activity_item.rb +1 -6
- data/app/models/uffizzi_core/build.rb +1 -1
- data/app/models/uffizzi_core/comment.rb +1 -1
- data/app/models/uffizzi_core/compose_file.rb +1 -1
- data/app/models/uffizzi_core/config_file.rb +1 -1
- data/app/models/uffizzi_core/container.rb +1 -1
- data/app/models/uffizzi_core/container_config_file.rb +1 -1
- data/app/models/uffizzi_core/coupon.rb +1 -1
- data/app/models/uffizzi_core/credential/github_container_registry.rb +4 -0
- data/app/models/uffizzi_core/credential.rb +3 -6
- data/app/models/uffizzi_core/deployment.rb +11 -2
- data/app/models/uffizzi_core/event.rb +1 -1
- data/app/models/uffizzi_core/invitation.rb +1 -1
- data/app/models/uffizzi_core/membership.rb +1 -1
- data/app/models/uffizzi_core/payment.rb +1 -1
- data/app/models/uffizzi_core/price.rb +1 -1
- data/app/models/uffizzi_core/product.rb +1 -1
- data/app/models/uffizzi_core/project.rb +5 -11
- data/app/models/uffizzi_core/rating.rb +1 -1
- data/app/models/uffizzi_core/repo/github_container_registry.rb +4 -0
- data/app/models/uffizzi_core/repo.rb +1 -11
- data/app/models/uffizzi_core/role.rb +2 -2
- data/app/models/uffizzi_core/secret.rb +9 -0
- data/app/models/uffizzi_core/template.rb +1 -1
- data/app/models/uffizzi_core/user.rb +1 -1
- data/app/models/uffizzi_core/user_project.rb +1 -1
- data/app/policies/uffizzi_core/api/cli/v1/account/credentials_policy.rb +8 -0
- data/app/policies/uffizzi_core/api/cli/v1/projects/deployments_policy.rb +4 -0
- data/app/repositories/uffizzi_core/credential_repo.rb +17 -22
- data/app/repositories/uffizzi_core/deployment_repo.rb +1 -0
- data/app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/container_serializer.rb +8 -0
- data/app/serializers/uffizzi_core/api/cli/v1/projects/secret_serializer.rb +5 -0
- data/app/serializers/uffizzi_core/controller/apply_config_file/config_file_serializer.rb +5 -0
- data/app/serializers/uffizzi_core/controller/create_credential/credential_serializer.rb +7 -3
- data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer/container_config_file_serializer/config_file_serializer.rb +8 -0
- data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer/container_config_file_serializer.rb +7 -0
- data/app/serializers/uffizzi_core/controller/deploy_containers/container_serializer.rb +4 -3
- data/app/services/uffizzi_core/activity_item_service.rb +16 -26
- data/app/services/uffizzi_core/compose_file/builders/container_builder_service.rb +11 -18
- data/app/services/uffizzi_core/compose_file/container_service.rb +13 -9
- data/app/services/uffizzi_core/compose_file/dependencies_service.rb +1 -0
- data/app/services/uffizzi_core/compose_file/services_options_service.rb +2 -2
- data/app/services/uffizzi_core/compose_file_service.rb +148 -0
- data/app/services/uffizzi_core/container_service.rb +1 -16
- data/app/services/uffizzi_core/controller_service.rb +7 -1
- data/app/services/uffizzi_core/credential_service.rb +2 -2
- data/app/services/uffizzi_core/deployment_service.rb +28 -7
- data/app/services/uffizzi_core/github_container_registry/credential_service.rb +24 -0
- data/app/services/uffizzi_core/manage_activity_items_service.rb +4 -19
- data/app/services/uffizzi_core/repo_service.rb +2 -137
- data/app/services/uffizzi_core/user_generator_service.rb +78 -0
- data/config/locales/en.activerecord.yml +5 -0
- data/config/locales/en.yml +4 -1
- data/config/routes.rb +6 -2
- data/db/migrate/20220309110201_remove_secrets_from_projects.rb +7 -0
- data/db/migrate/20220310110150_create_project_secrets.rb +14 -0
- data/db/migrate/20220329123323_rename_project_secrets_to_secrets.rb +7 -0
- data/db/migrate/20220329124542_add_resource_to_secrets.rb +7 -0
- data/db/migrate/20220329143241_remove_project_ref_from_secrets.rb +7 -0
- data/lib/tasks/uffizzi_core_tasks.rake +5 -0
- data/lib/uffizzi_core/engine.rb +35 -0
- data/lib/uffizzi_core/version.rb +1 -1
- data/lib/uffizzi_core.rb +1 -30
- data/swagger/v1/swagger.json +220 -11
- metadata +45 -25
- data/app/clients/uffizzi_core/github/app_client.rb +0 -19
- data/app/clients/uffizzi_core/github/installation_client.rb +0 -11
- data/app/clients/uffizzi_core/github/user_client.rb +0 -51
- data/app/errors/uffizzi_core/compose_file/not_found_error.rb +0 -4
- data/app/forms/uffizzi_core/api/cli/v1/project/delete_secret_form.rb +0 -27
- data/app/jobs/uffizzi_core/deployment/send_github_preview_message_job.rb +0 -13
- data/app/services/uffizzi_core/cli/compose_file_service.rb +0 -203
- data/app/services/uffizzi_core/compose_file/builders/github_repo_builder_service.rb +0 -59
- data/app/services/uffizzi_core/compose_file/services_options/build_service.rb +0 -93
- data/app/services/uffizzi_core/compose_file/update_service.rb +0 -29
- data/app/services/uffizzi_core/github/app_service.rb +0 -51
- data/app/services/uffizzi_core/github/credential_service.rb +0 -124
- data/app/services/uffizzi_core/github/message_service.rb +0 -20
- data/app/services/uffizzi_core/github_service.rb +0 -28
- data/app/services/uffizzi_core/user_access_service.rb +0 -14
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 49bbe08dfa41ecd2c4c9b6d2f488334db9ba40ec952b2a249d55e3be08b819aa
|
|
4
|
+
data.tar.gz: 37a606bcb357ef8b4b072b8be528c08a47569d8da3e3cf0fed00ec1aff110648
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0fcd5124498ab741cb1d03cada959eb0b821f15f8aede73f225ac0ac3a983b8deafecbe7a5905e8256c5b6f6c370348a4d85d6701b718b07bf9f56de2aea0fb2
|
|
7
|
+
data.tar.gz: 007c508efa144dfa7d617e36f12c00e3895cc871c9dc6cdfb00e4affd19eac3fc9ef4112898e7acf513408cae6077ff1e0bd5c2c8ffa1c71375c950c085be28e
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UffizziCore::GithubContainerRegistryClient
|
|
4
|
+
attr_accessor :token, :registry_url
|
|
5
|
+
|
|
6
|
+
def initialize(registry_url:, username:, password:)
|
|
7
|
+
@registry_url = registry_url
|
|
8
|
+
@username = username
|
|
9
|
+
@password = password
|
|
10
|
+
@token = access_token&.result&.token
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def access_token
|
|
14
|
+
service = URI.parse(registry_url).hostname
|
|
15
|
+
url = "/token?service=#{service}"
|
|
16
|
+
|
|
17
|
+
response = connection.get(url, {})
|
|
18
|
+
|
|
19
|
+
RequestResult.new(result: response.body)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def authentificated?
|
|
23
|
+
token.present?
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def manifests(image:, tag:)
|
|
27
|
+
url = "/v2/#{@username}/#{image}/manifests/#{tag}"
|
|
28
|
+
response = token_connection.get(url)
|
|
29
|
+
|
|
30
|
+
RequestResult.quiet.new(result: response.body, headers: response.headers)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def connection
|
|
36
|
+
Faraday.new(registry_url) do |conn|
|
|
37
|
+
conn.request(:basic_auth, @username, @password)
|
|
38
|
+
conn.request(:json)
|
|
39
|
+
conn.response(:json)
|
|
40
|
+
conn.adapter(Faraday.default_adapter)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def token_connection
|
|
45
|
+
Faraday.new(registry_url) do |conn|
|
|
46
|
+
conn.request(:authorization, 'Bearer', token)
|
|
47
|
+
conn.request(:json)
|
|
48
|
+
conn.response(:json)
|
|
49
|
+
conn.adapter(Faraday.default_adapter)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -4,7 +4,7 @@ module UffizziCore::DependencyInjectionConcern
|
|
|
4
4
|
def user_access_module
|
|
5
5
|
return unless module_exists?(:rbac)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
module_class(:rbac).new
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
private
|
|
@@ -14,6 +14,6 @@ module UffizziCore::DependencyInjectionConcern
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def module_class(module_name)
|
|
17
|
-
|
|
17
|
+
Rails.application.config.uffizzi_core[:dependencies][module_name]&.constantize
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -4,6 +4,17 @@
|
|
|
4
4
|
class UffizziCore::Api::Cli::V1::Account::CredentialsController < UffizziCore::Api::Cli::V1::Account::ApplicationController
|
|
5
5
|
before_action :authorize_uffizzi_core_api_cli_v1_account_credentials
|
|
6
6
|
|
|
7
|
+
# Get a list of accounts credential
|
|
8
|
+
#
|
|
9
|
+
# @path [GET] /api/cli/v1/account/credentials
|
|
10
|
+
#
|
|
11
|
+
# @parameter credential(required,body) [object<username:string, password: string, type:string>]
|
|
12
|
+
def index
|
|
13
|
+
credentials = resource_account.credentials.pluck(:type)
|
|
14
|
+
|
|
15
|
+
render json: { credentials: credentials }, status: :ok
|
|
16
|
+
end
|
|
17
|
+
|
|
7
18
|
# rubocop:disable Layout/LineLength
|
|
8
19
|
# Create account credential
|
|
9
20
|
#
|
|
@@ -14,26 +25,39 @@ class UffizziCore::Api::Cli::V1::Account::CredentialsController < UffizziCore::A
|
|
|
14
25
|
# @response [object<errors>] 422 Unprocessable entity
|
|
15
26
|
#
|
|
16
27
|
# @example
|
|
17
|
-
# type can be one of UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub, UffizziCore::Credential::Google
|
|
28
|
+
# type can be one of UffizziCore::Credential::Amazon, UffizziCore::Credential::Azure, UffizziCore::Credential::DockerHub, UffizziCore::Credential::Google, UffizziCore::Credential::GithubContainerRegistry
|
|
18
29
|
# rubocop:enable Layout/LineLength
|
|
19
30
|
def create
|
|
20
31
|
credential_form = UffizziCore::Api::Cli::V1::Account::Credential::CreateForm.new
|
|
21
32
|
credential_form.assign_attributes(credential_params)
|
|
22
33
|
credential_form.account = resource_account
|
|
23
|
-
credential_form.registry_url =
|
|
24
|
-
if credential_form.google?
|
|
25
|
-
credential_form.registry_url = Settings.google.registry_url
|
|
26
|
-
credential_form.username = '_json_key'
|
|
27
|
-
end
|
|
34
|
+
credential_form.registry_url = registry_url(credential_form)
|
|
35
|
+
credential_form.username = '_json_key' if credential_form.google?
|
|
28
36
|
credential_form.activate
|
|
29
37
|
|
|
30
|
-
if credential_form.save
|
|
31
|
-
UffizziCore::Account::CreateCredentialJob.perform_async(credential_form.id)
|
|
32
|
-
end
|
|
38
|
+
UffizziCore::Account::CreateCredentialJob.perform_async(credential_form.id) if credential_form.save
|
|
33
39
|
|
|
34
40
|
respond_with credential_form
|
|
35
41
|
end
|
|
36
42
|
|
|
43
|
+
# Check if credential of the type already exists in the account
|
|
44
|
+
#
|
|
45
|
+
# @path [GET] /api/cli/v1/account/credentials/{type}/check_credential
|
|
46
|
+
#
|
|
47
|
+
# @parameter credential(required,body) [object<type:string>]
|
|
48
|
+
# @response 422 Unprocessable entity
|
|
49
|
+
# @response 200 OK
|
|
50
|
+
def check_credential
|
|
51
|
+
credential_form = UffizziCore::Api::Cli::V1::Account::Credential::CheckCredentialForm.new
|
|
52
|
+
credential_form.type = params[:type]
|
|
53
|
+
credential_form.account = resource_account
|
|
54
|
+
if credential_form.valid?
|
|
55
|
+
respond_with credential_form
|
|
56
|
+
else
|
|
57
|
+
respond_with credential_form.errors, status: :unprocessable_entity
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
37
61
|
# Delete account credential
|
|
38
62
|
#
|
|
39
63
|
# @path [DELETE] /api/cli/v1/account/credentials/{type}
|
|
@@ -52,4 +76,16 @@ class UffizziCore::Api::Cli::V1::Account::CredentialsController < UffizziCore::A
|
|
|
52
76
|
def credential_params
|
|
53
77
|
params.require(:credential)
|
|
54
78
|
end
|
|
79
|
+
|
|
80
|
+
def registry_url(credential_form)
|
|
81
|
+
if credential_form.docker_hub?
|
|
82
|
+
Settings.docker_hub.registry_url
|
|
83
|
+
elsif credential_form.google?
|
|
84
|
+
Settings.google.registry_url
|
|
85
|
+
elsif credential_form.github_container_registry?
|
|
86
|
+
Settings.github_container_registry.registry_url
|
|
87
|
+
else
|
|
88
|
+
credential_form.registry_url
|
|
89
|
+
end
|
|
90
|
+
end
|
|
55
91
|
end
|
|
@@ -78,10 +78,10 @@ class UffizziCore::Api::Cli::V1::Projects::ComposeFilesController < UffizziCore:
|
|
|
78
78
|
def create_or_update_compose_file(params)
|
|
79
79
|
existing_compose_file = resource_project.compose_file
|
|
80
80
|
if existing_compose_file.present?
|
|
81
|
-
UffizziCore::
|
|
81
|
+
UffizziCore::ComposeFileService.update(existing_compose_file, params)
|
|
82
82
|
else
|
|
83
83
|
kind = UffizziCore::ComposeFile.kind.main
|
|
84
|
-
UffizziCore::
|
|
84
|
+
UffizziCore::ComposeFileService.create(params, kind)
|
|
85
85
|
end
|
|
86
86
|
end
|
|
87
87
|
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
class UffizziCore::Api::Cli::V1::Projects::Deployments::ApplicationController < UffizziCore::Api::Cli::V1::Projects::ApplicationController
|
|
4
4
|
def resource_deployment
|
|
5
|
-
@resource_deployment ||= resource_project.deployments.find(params[:deployment_id])
|
|
5
|
+
@resource_deployment ||= resource_project.deployments.active.find(params[:deployment_id])
|
|
6
6
|
end
|
|
7
7
|
end
|
|
@@ -56,6 +56,33 @@ class UffizziCore::Api::Cli::V1::Projects::DeploymentsController < UffizziCore::
|
|
|
56
56
|
respond_with deployment
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Update the deployment with new compose file
|
|
60
|
+
#
|
|
61
|
+
# @path [PUT] /api/cli/v1/projects/{project_slug}/deployments/{id}"
|
|
62
|
+
#
|
|
63
|
+
# @parameter project_slug(required,path) [string] The project slug
|
|
64
|
+
# @parameter params(required,body) [object<
|
|
65
|
+
# compose_file: object<path: string, source: string, content: string>,
|
|
66
|
+
# dependencies: Array<object<path: string, source: string, content: string>>>]
|
|
67
|
+
#
|
|
68
|
+
# @response [Deployment] 201 OK
|
|
69
|
+
# @response [object<errors: object<state: string>>] 422 Unprocessable Entity
|
|
70
|
+
# @response [object<errors: object<title: string>>] 404 Not found
|
|
71
|
+
# @response 401 Not authorized
|
|
72
|
+
def update
|
|
73
|
+
compose_file, errors = create_temporary_compose_file
|
|
74
|
+
return render_invalid_file if compose_file.invalid_file?
|
|
75
|
+
return render_errors(errors) if errors.present?
|
|
76
|
+
|
|
77
|
+
errors = check_credentials(compose_file)
|
|
78
|
+
return render_errors(errors) if errors.present?
|
|
79
|
+
|
|
80
|
+
deployment_id = params[:id]
|
|
81
|
+
deployment = UffizziCore::DeploymentService.update_from_compose(compose_file, resource_project, current_user, deployment_id)
|
|
82
|
+
|
|
83
|
+
respond_with deployment
|
|
84
|
+
end
|
|
85
|
+
|
|
59
86
|
# @path [POST] /api/cli/v1/projects/{project_slug}/deployments/{id}/deploy_containers
|
|
60
87
|
#
|
|
61
88
|
# @parameter project_slug(required,path) [string] The project slug
|
|
@@ -99,15 +126,7 @@ class UffizziCore::Api::Cli::V1::Projects::DeploymentsController < UffizziCore::
|
|
|
99
126
|
def find_or_create_compose_file
|
|
100
127
|
existing_compose_file = resource_project.compose_file
|
|
101
128
|
if compose_file_params.present?
|
|
102
|
-
|
|
103
|
-
project: resource_project,
|
|
104
|
-
user: current_user,
|
|
105
|
-
compose_file_params: compose_file_params,
|
|
106
|
-
dependencies: dependencies_params[:dependencies] || [],
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
kind = UffizziCore::ComposeFile.kind.temporary
|
|
110
|
-
UffizziCore::Cli::ComposeFileService.create(create_params, kind)
|
|
129
|
+
create_temporary_compose_file
|
|
111
130
|
else
|
|
112
131
|
raise ActiveRecord::RecordNotFound if existing_compose_file.blank?
|
|
113
132
|
|
|
@@ -116,6 +135,18 @@ class UffizziCore::Api::Cli::V1::Projects::DeploymentsController < UffizziCore::
|
|
|
116
135
|
end
|
|
117
136
|
end
|
|
118
137
|
|
|
138
|
+
def create_temporary_compose_file
|
|
139
|
+
create_params = {
|
|
140
|
+
project: resource_project,
|
|
141
|
+
user: current_user,
|
|
142
|
+
compose_file_params: compose_file_params,
|
|
143
|
+
dependencies: dependencies_params[:dependencies] || [],
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
kind = UffizziCore::ComposeFile.kind.temporary
|
|
147
|
+
UffizziCore::ComposeFileService.create(create_params, kind)
|
|
148
|
+
end
|
|
149
|
+
|
|
119
150
|
def check_credentials(compose_file)
|
|
120
151
|
credentials = resource_project.account.credentials
|
|
121
152
|
check_credentials_form = UffizziCore::Api::Cli::V1::ComposeFile::CheckCredentialsForm.new
|
|
@@ -127,7 +158,7 @@ class UffizziCore::Api::Cli::V1::Projects::DeploymentsController < UffizziCore::
|
|
|
127
158
|
end
|
|
128
159
|
|
|
129
160
|
def deployments
|
|
130
|
-
@deployments ||= resource_project.deployments.
|
|
161
|
+
@deployments ||= resource_project.deployments.existed
|
|
131
162
|
end
|
|
132
163
|
|
|
133
164
|
def deployment_params
|
|
@@ -8,13 +8,10 @@ class UffizziCore::Api::Cli::V1::Projects::SecretsController < UffizziCore::Api:
|
|
|
8
8
|
#
|
|
9
9
|
# @path [GET] /api/cli/v1/projects/{project_slug}/secrets
|
|
10
10
|
# @parameter project_slug(required,path) [string]
|
|
11
|
-
# @response [object<secrets: Array<object<name: string
|
|
11
|
+
# @response [object<secrets: Array<object<name: string, created_at: date, updated_at: date>>>] 200 OK
|
|
12
12
|
# @response 401 Not authorized
|
|
13
13
|
def index
|
|
14
|
-
|
|
15
|
-
secrets = project_secrets.map { |secret| { name: secret['name'] } }
|
|
16
|
-
|
|
17
|
-
render json: { secrets: secrets }, status: :ok
|
|
14
|
+
respond_with resource_project.secrets, root: :secrets
|
|
18
15
|
end
|
|
19
16
|
|
|
20
17
|
# Add secret to project
|
|
@@ -22,43 +19,38 @@ class UffizziCore::Api::Cli::V1::Projects::SecretsController < UffizziCore::Api:
|
|
|
22
19
|
# @path [POST] /api/cli/v1/projects/{project_slug}/secrets/bulk_create
|
|
23
20
|
# @parameter project_slug(required,path) [string]
|
|
24
21
|
# @parameter secrets(required,body) [object<secrets: Array<object <name: string, value: string>>>]
|
|
25
|
-
# @response [object<secrets: Array<object<name: string>>>] 201 Created
|
|
22
|
+
# @response [object<secrets: Array<object<name: string, created_at: date, updated_at: date>>>] 201 Created
|
|
26
23
|
# @response 422 A compose file already exists for this project
|
|
27
24
|
# @response 401 Not authorized
|
|
28
25
|
def bulk_create
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
secrets_form = UffizziCore::Api::Cli::V1::Secret::BulkAssignForm.new
|
|
27
|
+
secrets_form.secrets = resource_project.secrets
|
|
28
|
+
secrets_form.assign_secrets(secrets_params)
|
|
29
|
+
return respond_with secrets_form unless secrets_form.valid?
|
|
30
|
+
|
|
31
|
+
resource_project.secrets.replace(secrets_form.secrets)
|
|
32
32
|
|
|
33
|
-
UffizziCore::ProjectService.update_compose_secrets(
|
|
34
|
-
secrets = project_form.secrets.map { |secret| { name: secret['name'] } }
|
|
33
|
+
UffizziCore::ProjectService.update_compose_secrets(resource_project)
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
respond_with resource_project.secrets, root: :secrets
|
|
37
36
|
end
|
|
38
37
|
|
|
39
|
-
# Delete a secret from project by secret
|
|
38
|
+
# Delete a secret from project by secret name
|
|
40
39
|
#
|
|
41
|
-
# @path [DELETE] /api/cli/v1/projects/{project_slug}/secrets/{
|
|
40
|
+
# @path [DELETE] /api/cli/v1/projects/{project_slug}/secrets/{secret_name}
|
|
42
41
|
# @parameter project_slug(required,path) [string]
|
|
43
42
|
# @response [Project] 200 OK
|
|
44
|
-
# @response
|
|
43
|
+
# @response 404
|
|
45
44
|
# @response 401 Not authorized
|
|
46
45
|
def destroy
|
|
47
46
|
secret_name = CGI.unescape(params[:id])
|
|
48
|
-
secret =
|
|
49
|
-
project_form = resource_project.becomes(UffizziCore::Api::Cli::V1::Project::DeleteSecretForm)
|
|
50
|
-
project_form.secret = secret
|
|
47
|
+
secret = resource_project.secrets.find_by!(name: secret_name)
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
return respond_with project_form
|
|
54
|
-
end
|
|
49
|
+
UffizziCore::ProjectService.update_compose_secret_errors(resource_project, secret)
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
if project_form.save!(validate: false)
|
|
58
|
-
UffizziCore::ProjectService.update_compose_secret_errors(project_form, secret)
|
|
59
|
-
end
|
|
51
|
+
secret.destroy
|
|
60
52
|
|
|
61
|
-
|
|
53
|
+
head :no_content
|
|
62
54
|
end
|
|
63
55
|
|
|
64
56
|
private
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class UffizziCore::ApplicationController < ActionController::Base
|
|
4
|
-
include Pundit
|
|
4
|
+
include Pundit::Authorization
|
|
5
5
|
include UffizziCore::ResponseService
|
|
6
6
|
include UffizziCore::AuthManagement
|
|
7
7
|
include UffizziCore::AuthorizationConcern
|
|
@@ -12,11 +12,13 @@ class UffizziCore::ApplicationController < ActionController::Base
|
|
|
12
12
|
|
|
13
13
|
protect_from_forgery with: :exception
|
|
14
14
|
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
15
|
+
RESCUABLE_EXCEPTIONS = [RuntimeError, TypeError, NameError, ArgumentError, SyntaxError].freeze
|
|
16
|
+
rescue_from *RESCUABLE_EXCEPTIONS do |exception|
|
|
17
|
+
render_server_error(exception)
|
|
18
|
+
end
|
|
15
19
|
|
|
16
20
|
before_action :authenticate_request!
|
|
17
21
|
skip_before_action :verify_authenticity_token
|
|
18
|
-
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
|
19
|
-
|
|
20
22
|
respond_to :json
|
|
21
23
|
|
|
22
24
|
def policy_context
|
|
@@ -31,6 +33,10 @@ class UffizziCore::ApplicationController < ActionController::Base
|
|
|
31
33
|
render json: { errors: { title: ['Resource Not Found'] } }, status: :not_found
|
|
32
34
|
end
|
|
33
35
|
|
|
36
|
+
def render_server_error(error)
|
|
37
|
+
render json: { errors: { title: [error] } }, status: :internal_server_error
|
|
38
|
+
end
|
|
39
|
+
|
|
34
40
|
def render_errors(errors)
|
|
35
41
|
json = { errors: errors }
|
|
36
42
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UffizziCore::Api::Cli::V1::Account::Credential::CheckCredentialForm
|
|
4
|
+
include UffizziCore::ApplicationFormWithoutActiveRecord
|
|
5
|
+
|
|
6
|
+
attribute :type
|
|
7
|
+
attribute :account
|
|
8
|
+
|
|
9
|
+
validate :credential_exists?
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def credential_exists?
|
|
14
|
+
errors.add(:type, 'Credential of that type already exist.') if account.credentials.by_type(type).exists?
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -21,6 +21,6 @@ class UffizziCore::Api::Cli::V1::Account::Credential::CreateForm < UffizziCore::
|
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def credential_exists?
|
|
24
|
-
errors.add(:type, :exist) if account.credentials.
|
|
24
|
+
errors.add(:type, :exist) if account.credentials.by_type(type).exists?
|
|
25
25
|
end
|
|
26
26
|
end
|
|
@@ -12,9 +12,9 @@ class UffizziCore::Api::Cli::V1::ComposeFile::CheckCredentialsForm
|
|
|
12
12
|
|
|
13
13
|
def check_containers_credentials
|
|
14
14
|
compose_content = Base64.decode64(compose_file.content)
|
|
15
|
-
compose_data = UffizziCore::
|
|
15
|
+
compose_data = UffizziCore::ComposeFileService.parse(compose_content)
|
|
16
16
|
|
|
17
|
-
UffizziCore::
|
|
17
|
+
UffizziCore::ComposeFileService.containers_credentials(compose_data, credentials)
|
|
18
18
|
rescue UffizziCore::ComposeFile::CredentialError => e
|
|
19
19
|
errors.add(:credentials, e.message)
|
|
20
20
|
end
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
class UffizziCore::Api::Cli::V1::ComposeFile::CliForm
|
|
4
4
|
include UffizziCore::ApplicationFormWithoutActiveRecord
|
|
5
5
|
|
|
6
|
-
attribute :credential, UffizziCore::Credential
|
|
7
6
|
attribute :compose_content_data, Hash
|
|
8
7
|
attribute :compose_data, Hash
|
|
9
8
|
attribute :compose_dependencies, Array
|
|
@@ -13,27 +12,11 @@ class UffizziCore::Api::Cli::V1::ComposeFile::CliForm
|
|
|
13
12
|
validates :content, presence: true
|
|
14
13
|
|
|
15
14
|
validate :check_compose_parsed_data, if: -> { errors[:content].empty? }
|
|
16
|
-
validate :check_repositories, if: -> { credential.present? && errors[:content].empty? }
|
|
17
|
-
validate :check_branches, if: -> { credential.present? && errors[:content].empty? }
|
|
18
15
|
|
|
19
16
|
def check_compose_parsed_data
|
|
20
17
|
compose_content = Base64.decode64(content)
|
|
21
|
-
self.compose_data = UffizziCore::
|
|
18
|
+
self.compose_data = UffizziCore::ComposeFileService.parse(compose_content)
|
|
22
19
|
rescue UffizziCore::ComposeFile::ParseError => e
|
|
23
20
|
errors.add(:content, e.message)
|
|
24
21
|
end
|
|
25
|
-
|
|
26
|
-
def check_repositories
|
|
27
|
-
self.compose_repositories = UffizziCore::Cli::ComposeFileService.load_repositories(compose_data, credential)
|
|
28
|
-
rescue UffizziCore::ComposeFile::NotFoundError => e
|
|
29
|
-
errors.add(:content, e.message)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def check_branches
|
|
33
|
-
return if compose_repositories.blank?
|
|
34
|
-
|
|
35
|
-
UffizziCore::Cli::ComposeFileService.check_github_branches(compose_data, compose_repositories, credential)
|
|
36
|
-
rescue UffizziCore::ComposeFile::NotFoundError => e
|
|
37
|
-
errors.add(:content, e.message)
|
|
38
|
-
end
|
|
39
22
|
end
|
|
@@ -19,7 +19,7 @@ class UffizziCore::Api::Cli::V1::ComposeFile::TemplateForm
|
|
|
19
19
|
validate :check_template_attributes
|
|
20
20
|
|
|
21
21
|
def assign_template_attributes!
|
|
22
|
-
self.template_attributes = UffizziCore::
|
|
22
|
+
self.template_attributes = UffizziCore::ComposeFileService.build_template_attributes(
|
|
23
23
|
compose_data,
|
|
24
24
|
source,
|
|
25
25
|
credentials,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UffizziCore::Api::Cli::V1::Deployment::UpdateForm < UffizziCore::Deployment
|
|
4
|
+
include UffizziCore::ApplicationForm
|
|
5
|
+
|
|
6
|
+
permit containers_attributes: [
|
|
7
|
+
:image,
|
|
8
|
+
:name,
|
|
9
|
+
:tag,
|
|
10
|
+
:port,
|
|
11
|
+
:public,
|
|
12
|
+
:memory_limit,
|
|
13
|
+
:memory_request,
|
|
14
|
+
:entrypoint,
|
|
15
|
+
:command,
|
|
16
|
+
:receive_incoming_requests,
|
|
17
|
+
:continuously_deploy,
|
|
18
|
+
{ variables: [:name, :value],
|
|
19
|
+
secret_variables: [:name, :value],
|
|
20
|
+
repo_attributes: [
|
|
21
|
+
:namespace,
|
|
22
|
+
:name,
|
|
23
|
+
:slug,
|
|
24
|
+
:type,
|
|
25
|
+
:description,
|
|
26
|
+
:is_private,
|
|
27
|
+
:repository_id,
|
|
28
|
+
:branch,
|
|
29
|
+
:kind,
|
|
30
|
+
:dockerfile_path,
|
|
31
|
+
:dockerfile_context_path,
|
|
32
|
+
:deploy_preview_when_pull_request_is_opened,
|
|
33
|
+
:delete_preview_when_pull_request_is_closed,
|
|
34
|
+
:deploy_preview_when_image_tag_is_created,
|
|
35
|
+
:delete_preview_when_image_tag_is_updated,
|
|
36
|
+
:share_to_github,
|
|
37
|
+
:delete_preview_after,
|
|
38
|
+
{ args: [:name, :value] },
|
|
39
|
+
],
|
|
40
|
+
container_config_files_attributes: [
|
|
41
|
+
:config_file_id,
|
|
42
|
+
:mount_path,
|
|
43
|
+
] },
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
validate :check_all_containers_have_unique_ports
|
|
47
|
+
validate :check_exists_ingress_container
|
|
48
|
+
validate :check_max_memory_limit
|
|
49
|
+
validate :check_max_memory_request
|
|
50
|
+
|
|
51
|
+
def assign_dependences!(project, user)
|
|
52
|
+
self.project = project
|
|
53
|
+
|
|
54
|
+
self.containers = containers.map do |container|
|
|
55
|
+
container.repo.project = project if !container.repo.nil?
|
|
56
|
+
|
|
57
|
+
container
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
self.deployed_by = user
|
|
61
|
+
|
|
62
|
+
self
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def check_all_containers_have_unique_ports
|
|
68
|
+
active_containers = containers.select(&:active?)
|
|
69
|
+
|
|
70
|
+
errors.add(:containers, :duplicate_ports) unless UffizziCore::DeploymentService.all_containers_have_unique_ports?(active_containers)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def check_exists_ingress_container
|
|
74
|
+
active_containers = containers.select(&:active?)
|
|
75
|
+
|
|
76
|
+
errors.add(:containers, :incorrect_ingress_container) unless UffizziCore::DeploymentService.ingress_container?(active_containers)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def check_max_memory_limit
|
|
80
|
+
return if UffizziCore::DeploymentService.valid_containers_memory_limit?(self)
|
|
81
|
+
|
|
82
|
+
errors.add(:containers, :max_memory_limit_error, max: project.account.container_memory_limit)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def check_max_memory_request
|
|
86
|
+
return if UffizziCore::DeploymentService.valid_containers_memory_request?(self)
|
|
87
|
+
|
|
88
|
+
errors.add(:containers, :max_memory_request_error, max: project.account.container_memory_limit)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -2,39 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
class UffizziCore::Api::Cli::V1::Project::UpdateForm < UffizziCore::Project
|
|
4
4
|
include UffizziCore::ApplicationForm
|
|
5
|
-
MAX_SECRET_KEY_LENGTH = 256
|
|
6
5
|
|
|
7
|
-
permit :name, :slug, :description
|
|
6
|
+
permit :name, :slug, :description
|
|
8
7
|
|
|
9
8
|
validates :name, presence: true, uniqueness: { scope: :account }
|
|
10
9
|
validates :slug, presence: true, uniqueness: true
|
|
11
|
-
|
|
12
|
-
validate :check_duplicates
|
|
13
|
-
validate :check_length
|
|
14
|
-
|
|
15
|
-
def assign_secrets!(new_secrets)
|
|
16
|
-
existing_secrets = secrets.presence || []
|
|
17
|
-
|
|
18
|
-
self.secrets = existing_secrets.union(new_secrets)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def check_duplicates
|
|
24
|
-
duplicates = []
|
|
25
|
-
groupped_secrets = secrets.group_by { |secret| secret['name'] }
|
|
26
|
-
groupped_secrets.each_pair do |key, value|
|
|
27
|
-
duplicates << key if value.size > 1
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
error_message = I18n.t('secrets.duplicates_exists', secrets: duplicates.join(', '))
|
|
31
|
-
errors.add(:secrets, error_message) if duplicates.present?
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def check_length
|
|
35
|
-
secrets_with_invalid_key_length = secrets.select { |secret| secret['name'].length > MAX_SECRET_KEY_LENGTH }
|
|
36
|
-
|
|
37
|
-
error_message = I18n.t('secrets.invalid_key_length')
|
|
38
|
-
errors.add(:secrets, error_message) if secrets_with_invalid_key_length.present?
|
|
39
|
-
end
|
|
40
10
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class UffizziCore::Api::Cli::V1::Secret::BulkAssignForm
|
|
4
|
+
include UffizziCore::ApplicationFormWithoutActiveRecord
|
|
5
|
+
MAX_SECRET_KEY_LENGTH = 256
|
|
6
|
+
|
|
7
|
+
attribute :secrets, Array
|
|
8
|
+
validate :check_duplicates
|
|
9
|
+
validate :check_length
|
|
10
|
+
|
|
11
|
+
def assign_secrets(new_secrets)
|
|
12
|
+
return if new_secrets.blank?
|
|
13
|
+
|
|
14
|
+
new_secrets.each do |new_secret|
|
|
15
|
+
secret = UffizziCore::Secret.new(name: new_secret['name'], value: new_secret['value'])
|
|
16
|
+
secrets.append(secret)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def check_duplicates
|
|
23
|
+
duplicates = []
|
|
24
|
+
groupped_secrets = secrets.group_by { |secret| secret['name'] }
|
|
25
|
+
groupped_secrets.each_pair do |key, value|
|
|
26
|
+
duplicates << key if value.size > 1
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
error_message = I18n.t('secrets.duplicates_exists', secrets: duplicates.join(', '))
|
|
30
|
+
errors.add(:secrets, error_message) if duplicates.present?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def check_length
|
|
34
|
+
secrets_with_invalid_key_length = secrets.select { |secret| secret['name'].length > MAX_SECRET_KEY_LENGTH }
|
|
35
|
+
|
|
36
|
+
error_message = I18n.t('secrets.invalid_key_length')
|
|
37
|
+
errors.add(:secrets, error_message) if secrets_with_invalid_key_length.present?
|
|
38
|
+
end
|
|
39
|
+
end
|