uffizzi_core 2.1.29 → 2.2.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/app/clients/uffizzi_core/controller_client.rb +40 -23
  3. data/app/controller_modules/uffizzi_core/api/cli/v1/projects/clusters_controller_module.rb +9 -0
  4. data/app/controllers/concerns/uffizzi_core/auth_management.rb +17 -1
  5. data/app/controllers/uffizzi_core/api/cli/v1/accounts/projects_controller.rb +7 -1
  6. data/app/controllers/uffizzi_core/api/cli/v1/accounts_controller.rb +45 -0
  7. data/app/controllers/uffizzi_core/api/cli/v1/projects/clusters_controller.rb +46 -0
  8. data/app/forms/uffizzi_core/api/cli/v1/cluster/create_form.rb +19 -0
  9. data/app/jobs/uffizzi_core/cluster/delete_job.rb +12 -0
  10. data/app/jobs/uffizzi_core/cluster/deploy_job.rb +11 -0
  11. data/app/jobs/uffizzi_core/cluster/manage_deploying_job.rb +11 -0
  12. data/app/jobs/uffizzi_core/config_file/apply_job.rb +1 -1
  13. data/app/jobs/uffizzi_core/deployment/create_credential_job.rb +1 -1
  14. data/app/jobs/uffizzi_core/deployment/create_job.rb +1 -1
  15. data/app/jobs/uffizzi_core/deployment/delete_job.rb +2 -1
  16. data/app/jobs/uffizzi_core/deployment/deploy_containers_job.rb +1 -1
  17. data/app/jobs/uffizzi_core/deployment/update_credential_job.rb +1 -1
  18. data/app/lib/uffizzi_core/concerns/models/account.rb +1 -0
  19. data/app/lib/uffizzi_core/concerns/models/cluster.rb +54 -0
  20. data/app/lib/uffizzi_core/concerns/models/deployment.rb +4 -0
  21. data/app/lib/uffizzi_core/concerns/models/project.rb +1 -0
  22. data/app/lib/uffizzi_core/concerns/models/user.rb +1 -0
  23. data/app/models/uffizzi_core/cluster.rb +5 -0
  24. data/app/policies/uffizzi_core/api/cli/v1/accounts/projects_policy.rb +4 -0
  25. data/app/policies/uffizzi_core/api/cli/v1/accounts_policy.rb +11 -0
  26. data/app/policies/uffizzi_core/api/cli/v1/projects/clusters_policy.rb +19 -0
  27. data/app/repositories/uffizzi_core/account_repo.rb +1 -0
  28. data/app/repositories/uffizzi_core/cluster_repo.rb +10 -0
  29. data/app/serializers/uffizzi_core/api/cli/v1/account_serializer.rb +9 -0
  30. data/app/serializers/uffizzi_core/api/cli/v1/projects/cluster_serializer.rb +7 -0
  31. data/app/serializers/uffizzi_core/controller/create_cluster/cluster_serializer.rb +9 -0
  32. data/app/services/uffizzi_core/cluster_service.rb +44 -0
  33. data/app/services/uffizzi_core/controller_service.rb +43 -21
  34. data/app/services/uffizzi_core/logs_service.rb +1 -1
  35. data/config/routes.rb +5 -2
  36. data/db/migrate/20230613101901_create_clusters.rb +19 -0
  37. data/lib/uffizzi_core/version.rb +1 -1
  38. data/lib/uffizzi_core.rb +1 -0
  39. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c97fb24aa0b59fec57a431af483f81a030afa9260fc7f0db21488edc0ad88842
4
- data.tar.gz: e96d6f8d5b97de2d35e5311ab4cacb081a09713f1c75452d3a895c3caf73c190
3
+ metadata.gz: a3857b5cc3f90f134b4ce7275290e460c4abf7868dcdc8d3940390efbab73fc9
4
+ data.tar.gz: 99d1c61e2af05d005a2c30314bf726ed2f68442c8cafaf1c036fc5294294d286
5
5
  SHA512:
6
- metadata.gz: 94efb8590f5b3575978921342816d7fe48355af568f2c6e693093b64b1ac8fae5533f859e5567b201d3d67254f107fc703167ad59b1b6016376b88ab3bab5398
7
- data.tar.gz: f258d75612c7abfda4ca2aea0bafecba4274d91cf3ecbe663051f5ef6a0614177d3666931a9317d7d6fcf7c11a2c8c22fb2ce38a1ffee4760e1d29e67f93cf5b
6
+ metadata.gz: 9437c1c50d81bd7e6de3419ae81ef1d4035204b1e153249ae2a233918faf36e9267f6b58b200a73a77b335f5d03f4c2f7b1aebf39d7ed08e765b31941663da3d
7
+ data.tar.gz: 574c4e039f9f5a6ad4204b696ba6778eeb5ed88d729a5cda724b9d3a20e790f01547cf4b16c3a88bdd105da388bcd2c99e6e00194a8210d5c407845055bc1d93
@@ -1,28 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UffizziCore::ControllerClient
4
+ class ConnectionError < StandardError; end
5
+
4
6
  attr_accessor :connection
5
7
 
6
- def initialize
7
- @connection = build_connection
8
+ def initialize(connection_settings)
9
+ @connection = build_connection(connection_settings)
8
10
  end
9
11
 
10
12
  def apply_config_file(deployment_id:, config_file_id:, body:)
11
13
  connection.post("/deployments/#{deployment_id}/config_files/#{config_file_id}", body)
12
14
  end
13
15
 
14
- def deployment(deployment_id:)
15
- get("/deployments/#{deployment_id}")
16
- end
17
-
18
- def create_deployment(deployment_id:, body:)
19
- connection.post("/deployments/#{deployment_id}", body)
20
- end
21
-
22
- def delete_deployment(deployment_id:)
23
- connection.delete("/deployments/#{deployment_id}")
24
- end
25
-
26
16
  def deployment_containers(deployment_id:)
27
17
  get("/deployments/#{deployment_id}/containers")
28
18
  end
@@ -68,34 +58,61 @@ class UffizziCore::ControllerClient
68
58
  get('/deployments/usage_metrics/containers', query_params)
69
59
  end
70
60
 
61
+ def create_namespace(body:)
62
+ post('/namespaces', body)
63
+ end
64
+
65
+ def namespace(namespace:)
66
+ get("/namespaces/#{namespace}")
67
+ end
68
+
69
+ def delete_namespace(namespace:)
70
+ connection.delete("/namespaces/#{namespace}")
71
+ end
72
+
73
+ def create_cluster(namespace:, body:)
74
+ post("/namespaces/#{namespace}/cluster", body)
75
+ end
76
+
77
+ def show_cluster(namespace:, name:)
78
+ get("/namespaces/#{namespace}/cluster/#{name}")
79
+ end
80
+
71
81
  private
72
82
 
73
83
  def get(url, params = {})
74
- response = connection.get(url, params)
84
+ make_request(:get, url, params)
85
+ end
86
+
87
+ def post(url, params = {})
88
+ make_request(:post, url, params)
89
+ end
90
+
91
+ def make_request(method, url, params)
92
+ response = connection.send(method, url, params)
75
93
  body = response.body
76
94
  underscored_body = UffizziCore::Converters.deep_underscore_keys(body)
77
95
 
78
96
  RequestResult.quiet.new(code: response.status, result: underscored_body)
97
+ rescue Faraday::ServerError
98
+ raise ConnectionError
79
99
  end
80
100
 
81
- def build_connection
82
- controller = Settings.controller
83
- login = controller.login
84
- password = controller.password
85
- url = controller.url
86
- connection = controller.connection
101
+ def build_connection(settings)
102
+ connection = settings.connection
87
103
  handled_exceptions = Faraday::Request::Retry::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed]
88
104
 
89
- Faraday.new(url) do |conn|
105
+ Faraday.new(settings.url) do |conn|
90
106
  conn.options.timeout = connection.timeout
91
107
  conn.options.open_timeout = connection.open_timeout
92
- conn.request(:basic_auth, login, password)
108
+ conn.request(:basic_auth, settings.login, settings.password)
93
109
  conn.request(:json)
94
110
  conn.request(:retry,
95
111
  max: connection.retires_count,
96
112
  interval: connection.next_retry_timeout_seconds,
97
113
  exceptions: handled_exceptions)
98
114
  conn.response(:json)
115
+ conn.response(:raise_error)
99
116
  conn.adapter(Faraday.default_adapter)
100
117
  end
101
118
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UffizziCore::Api::Cli::V1::Projects::ClustersControllerModule
4
+ private
5
+
6
+ def update_show_trial_quota_exceeded_warning; end
7
+
8
+ def stop_if_deployment_forbidden; end
9
+ end
@@ -14,7 +14,23 @@ module UffizziCore::AuthManagement
14
14
  end
15
15
 
16
16
  def current_user
17
- @current_user ||= UffizziCore::User.find_by(id: session[:user_id])
17
+ @current_user ||= UffizziCore::User.find_by(id: current_user_id)
18
+ end
19
+
20
+ def auth_token
21
+ header = request.headers['Authorization']
22
+ header&.split(' ')&.last
23
+ end
24
+
25
+ def current_user_id
26
+ return session[:user_id] if session[:user_id].present?
27
+ return unless auth_token.present?
28
+
29
+ decoded_token = UffizziCore::TokenService.decode(auth_token)
30
+ return unless decoded_token
31
+ return if decoded_token.first['expires_at'] < DateTime.now
32
+
33
+ decoded_token.first['user_id']
18
34
  end
19
35
 
20
36
  def authenticate_request!
@@ -5,6 +5,12 @@
5
5
  class UffizziCore::Api::Cli::V1::Accounts::ProjectsController < UffizziCore::Api::Cli::V1::Accounts::ApplicationController
6
6
  before_action :authorize_uffizzi_core_api_cli_v1_accounts_projects
7
7
 
8
+ def index
9
+ projects = resource_account.projects.active
10
+
11
+ respond_with projects, each_serializer: UffizziCore::Api::Cli::V1::ShortProjectSerializer
12
+ end
13
+
8
14
  # Create a project
9
15
  #
10
16
  # @path [POST] /api/cli/v1/accounts/{account_id}/projects
@@ -17,7 +23,7 @@ class UffizziCore::Api::Cli::V1::Accounts::ProjectsController < UffizziCore::Api
17
23
 
18
24
  def create
19
25
  project_form = UffizziCore::Api::Cli::V1::Project::CreateForm.new(project_params)
20
- project_form.account = current_user.accounts.find(params[:account_id])
26
+ project_form.account = resource_account
21
27
  UffizziCore::ProjectService.add_users_to_project!(project_form, project_form.account) if project_form.save
22
28
 
23
29
  respond_with project_form
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @resource Project
4
+
5
+ class UffizziCore::Api::Cli::V1::AccountsController < UffizziCore::Api::Cli::V1::ApplicationController
6
+ before_action :authorize_uffizzi_core_api_cli_v1_accounts
7
+
8
+ # Get accounts of current user
9
+ #
10
+ # @path [GET] /api/cli/v1/accounts
11
+ #
12
+ # @response [object<accounts: Array<object<id: integer, name: string>> >] 200 OK
13
+ # @response 401 Not authorized
14
+ def index
15
+ accounts = current_user.accounts.order(name: :desc)
16
+
17
+ respond_with accounts
18
+ end
19
+
20
+ # Get account by name
21
+ #
22
+ # @path [GET] /api/cli/v1/accounts/{name}
23
+ #
24
+ # @response [object<account: <object<id: integer, name: string, projects: Array<object<id: integer, slug: string>>>> >] 200 OK
25
+ # @response 401 Not authorized
26
+ def show
27
+ respond_with resource_account
28
+ end
29
+
30
+ private
31
+
32
+ def policy_context
33
+ account = resource_account || current_user.default_account
34
+
35
+ UffizziCore::AccountContext.new(current_user, user_access_module, account, params)
36
+ end
37
+
38
+ def resource_account
39
+ @resource_account ||= if params[:name]
40
+ current_user.accounts.find_by!(name: params[:name])
41
+ else
42
+ current_user.default_account
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Projects::ClustersController < UffizziCore::Api::Cli::V1::Projects::ApplicationController
4
+ include UffizziCore::Api::Cli::V1::Projects::ClustersControllerModule
5
+
6
+ before_action :authorize_uffizzi_core_api_cli_v1_projects_clusters
7
+ before_action :stop_if_deployment_forbidden, only: [:create]
8
+ after_action :update_show_trial_quota_exceeded_warning, only: [:create, :destroy]
9
+
10
+ def index
11
+ clusters = resource_project.clusters.enabled
12
+
13
+ respond_with clusters
14
+ end
15
+
16
+ def create
17
+ cluster_form = UffizziCore::Api::Cli::V1::Cluster::CreateForm.new(cluster_params)
18
+ cluster_form.project = resource_project
19
+ cluster_form.deployed_by = current_user
20
+ return respond_with cluster_form unless cluster_form.save
21
+
22
+ UffizziCore::ClusterService.start_deploy(cluster_form)
23
+
24
+ respond_with cluster_form
25
+ end
26
+
27
+ def show
28
+ respond_with resource_cluster
29
+ end
30
+
31
+ def destroy
32
+ resource_cluster.disable!
33
+
34
+ head(:no_content)
35
+ end
36
+
37
+ private
38
+
39
+ def resource_cluster
40
+ @resource_cluster ||= resource_project.clusters.enabled.find_by!(name: params[:name])
41
+ end
42
+
43
+ def cluster_params
44
+ params.require(:cluster)
45
+ end
46
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Cluster::CreateForm < UffizziCore::Cluster
4
+ include UffizziCore::ApplicationForm
5
+
6
+ permit :name, :manifest
7
+
8
+ validate :check_manifest, if: -> { manifest.present? }
9
+
10
+ private
11
+
12
+ def check_manifest
13
+ YAML.load_stream(manifest)
14
+ rescue Psych::SyntaxError => e
15
+ err = [e.problem, e.context].compact.join(' ')
16
+
17
+ errors.add(:manifest, err)
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Cluster::DeleteJob < UffizziCore::ApplicationJob
4
+ sidekiq_options queue: :deployments, retry: 5
5
+
6
+ def perform(id)
7
+ Rails.logger.info("DEPLOYMENT_PROCESS cluster_id=#{id} DeleteJob")
8
+
9
+ cluster = UffizziCore::Cluster.find(id)
10
+ UffizziCore::ControllerService.delete_namespace(cluster)
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Cluster::DeployJob < UffizziCore::ApplicationJob
4
+ sidekiq_options queue: :deployments, retry: 5
5
+
6
+ def perform(id)
7
+ cluster = UffizziCore::Cluster.find(id)
8
+
9
+ UffizziCore::ClusterService.deploy_cluster(cluster)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Cluster::ManageDeployingJob < UffizziCore::ApplicationJob
4
+ sidekiq_options queue: :deployments, retry: 5
5
+
6
+ def perform(id, try = 1)
7
+ cluster = UffizziCore::Cluster.find(id)
8
+
9
+ UffizziCore::ClusterService.manage_deploying(cluster, try)
10
+ end
11
+ end
@@ -28,7 +28,7 @@ class UffizziCore::ConfigFile::ApplyJob < UffizziCore::ApplicationJob
28
28
  return
29
29
  end
30
30
 
31
- unless UffizziCore::ControllerService.deployment_exists?(deployment)
31
+ unless UffizziCore::ControllerService.namespace_exists?(deployment)
32
32
  raise UffizziCore::DeploymentNotFoundError,
33
33
  deployment_id
34
34
  end
@@ -29,7 +29,7 @@ class UffizziCore::Deployment::CreateCredentialJob < UffizziCore::ApplicationJob
29
29
  return
30
30
  end
31
31
 
32
- unless UffizziCore::ControllerService.deployment_exists?(deployment)
32
+ unless UffizziCore::ControllerService.namespace_exists?(deployment)
33
33
  raise UffizziCore::DeploymentNotFoundError,
34
34
  deployment_id
35
35
  end
@@ -8,7 +8,7 @@ class UffizziCore::Deployment::CreateJob < UffizziCore::ApplicationJob
8
8
 
9
9
  deployment = UffizziCore::Deployment.find(id)
10
10
 
11
- UffizziCore::ControllerService.create_deployment(deployment)
11
+ UffizziCore::ControllerService.create_namespace(deployment)
12
12
 
13
13
  UffizziCore::Deployment::CreateCredentialsJob.perform_async(deployment.id)
14
14
  end
@@ -6,6 +6,7 @@ class UffizziCore::Deployment::DeleteJob < UffizziCore::ApplicationJob
6
6
  def perform(id)
7
7
  Rails.logger.info("DEPLOYMENT_PROCESS deployment_id=#{id} DeleteJob")
8
8
 
9
- UffizziCore::ControllerService.delete_deployment(id)
9
+ deployment = UffizziCore::Deployment.find(id)
10
+ UffizziCore::ControllerService.delete_namespace(deployment)
10
11
  end
11
12
  end
@@ -27,7 +27,7 @@ class UffizziCore::Deployment::DeployContainersJob < UffizziCore::ApplicationJob
27
27
  return
28
28
  end
29
29
 
30
- raise UffizziCore::DeploymentNotFoundError, id unless UffizziCore::ControllerService.deployment_exists?(deployment)
30
+ raise UffizziCore::DeploymentNotFoundError, id unless UffizziCore::ControllerService.namespace_exists?(deployment)
31
31
 
32
32
  UffizziCore::DeploymentService.deploy_containers(deployment, repeated)
33
33
  end
@@ -29,7 +29,7 @@ class UffizziCore::Deployment::UpdateCredentialJob < UffizziCore::ApplicationJob
29
29
  return
30
30
  end
31
31
 
32
- unless UffizziCore::ControllerService.deployment_exists?(deployment)
32
+ unless UffizziCore::ControllerService.namespace_exists?(deployment)
33
33
  raise UffizziCore::DeploymentNotFoundError,
34
34
  deployment_id
35
35
  end
@@ -23,6 +23,7 @@ module UffizziCore::Concerns::Models::Account
23
23
  has_many :projects, dependent: :destroy
24
24
  has_many :deployments, through: :projects
25
25
  has_many :payments, dependent: :destroy
26
+ has_many :clusters, through: :projects
26
27
 
27
28
  aasm(:sso_state) do
28
29
  state :connection_not_configured, initial: true
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UffizziCore::Concerns::Models::Cluster
4
+ extend ActiveSupport::Concern
5
+ include UffizziCore::ClusterRepo
6
+
7
+ included do
8
+ include AASM
9
+
10
+ self.table_name = UffizziCore.table_names[:clusters]
11
+
12
+ belongs_to :project, class_name: UffizziCore::Project.name
13
+ belongs_to :deployed_by, class_name: UffizziCore::User.name, foreign_key: :deployed_by_id, optional: true
14
+ validates_uniqueness_of :name, conditions: -> { enabled }, scope: :project_id
15
+ validates :name, presence: true, format: { with: /\A[a-zA-Z0-9-]*\z/ }
16
+
17
+ aasm(:state) do
18
+ state :deploying_namespace, initial: true
19
+ state :failed_deploy_namespace
20
+ state :deploying
21
+ state :deployed
22
+ state :failed
23
+ state :disabled
24
+
25
+ event :start_deploying do
26
+ transitions from: [:deploying_namespace], to: :deploying
27
+ end
28
+
29
+ event :fail_deploy_namespace do
30
+ transitions from: [:deploying_namespace], to: :failed_deploy_namespace
31
+ end
32
+
33
+ event :finish_deploy do
34
+ transitions from: [:deploying], to: :deployed
35
+ end
36
+
37
+ event :fail do
38
+ transitions from: [:deploying], to: :failed
39
+ end
40
+
41
+ event :disable, after: :after_disable do
42
+ transitions from: [:failed_deploy_namespace, :deploying, :deployed, :failed], to: :disabled
43
+ end
44
+ end
45
+
46
+ def after_disable
47
+ UffizziCore::Cluster::DeleteJob.perform_async(id)
48
+ end
49
+
50
+ def namespace
51
+ "cluster-#{id}"
52
+ end
53
+ end
54
+ end
@@ -72,6 +72,10 @@ module UffizziCore::Concerns::Models::Deployment
72
72
  def preview_url
73
73
  "#{subdomain}.#{Settings.app.managed_dns_zone}"
74
74
  end
75
+
76
+ def namespace
77
+ "deployment-#{id}"
78
+ end
75
79
  end
76
80
  # rubocop:enable Metrics/BlockLength
77
81
  end
@@ -22,6 +22,7 @@ module UffizziCore::Concerns::Models::Project
22
22
  has_many :compose_files, dependent: :destroy
23
23
  has_many :secrets, dependent: :destroy, as: :resource
24
24
  has_many :host_volume_files, dependent: :destroy
25
+ has_many :clusters, dependent: :destroy
25
26
 
26
27
  validates :name, presence: true, uniqueness: { scope: :account, message: 'Name already exists' }
27
28
  validates :slug, presence: true, uniqueness: { message: 'Project slug already taken' }
@@ -20,6 +20,7 @@ module UffizziCore::Concerns::Models::User
20
20
  has_many :user_projects, dependent: :destroy
21
21
  has_many :projects, through: :user_projects
22
22
  has_many :deployments, class_name: UffizziCore::Deployment.name, foreign_key: :deployed_by_id, dependent: :nullify
23
+ has_many :clusters, foreign_key: :deployed_by_id
23
24
 
24
25
  has_one_attached :avatar
25
26
 
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Cluster < ApplicationRecord
4
+ include UffizziCore::Concerns::Models::Cluster
5
+ end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class UffizziCore::Api::Cli::V1::Accounts::ProjectsPolicy < UffizziCore::ApplicationPolicy
4
+ def index?
5
+ context.user_access_module.any_access_to_account?(context.user, context.account)
6
+ end
7
+
4
8
  def create?
5
9
  context.user_access_module.admin_or_developer_access_to_account?(context.user, context.account)
6
10
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::AccountsPolicy < UffizziCore::ApplicationPolicy
4
+ def index?
5
+ context.user.present?
6
+ end
7
+
8
+ def show?
9
+ context.user_access_module.any_access_to_account?(context.user, context.account)
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Projects::ClustersPolicy < UffizziCore::ApplicationPolicy
4
+ def index?
5
+ context.user_access_module.any_access_to_project?(context.user, context.project)
6
+ end
7
+
8
+ def show?
9
+ context.user_access_module.any_access_to_project?(context.user, context.project)
10
+ end
11
+
12
+ def create?
13
+ context.user_access_module.admin_or_developer_access_to_project?(context.user, context.project)
14
+ end
15
+
16
+ def destroy?
17
+ context.user_access_module.admin_or_developer_access_to_project?(context.user, context.project)
18
+ end
19
+ end
@@ -6,5 +6,6 @@ module UffizziCore::AccountRepo
6
6
  included do
7
7
  scope :by_kind, ->(kind) { where(kind: kind) }
8
8
  scope :personal, -> { by_kind(UffizziCore::Account.kind.personal) }
9
+ scope :organizational, -> { by_kind(UffizziCore::Account.kind.organizational) }
9
10
  end
10
11
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UffizziCore::ClusterRepo
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ scope :deployed, -> { where(state: UffizziCore::Cluster::STATE_DEPLOYED) }
8
+ scope :enabled, -> { where.not(state: UffizziCore::Cluster::STATE_DISABLED) }
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::AccountSerializer < UffizziCore::BaseSerializer
4
+ type :account
5
+
6
+ has_many :projects
7
+
8
+ attributes :id, :name
9
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Projects::ClusterSerializer < UffizziCore::BaseSerializer
4
+ type :cluster
5
+
6
+ attributes :name, :state, :kubeconfig
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Controller::CreateCluster::ClusterSerializer < UffizziCore::BaseSerializer
4
+ attributes :name, :manifest, :base_ingress_host
5
+
6
+ def base_ingress_host
7
+ Settings.app.managed_dns_zone
8
+ end
9
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ClusterService
4
+ class << self
5
+ def start_deploy(cluster)
6
+ UffizziCore::Cluster::DeployJob.perform_async(cluster.id)
7
+ end
8
+
9
+ def deploy_cluster(cluster)
10
+ begin
11
+ UffizziCore::ControllerService.create_namespace(cluster)
12
+ rescue UffizziCore::ControllerClient::ConnectionError
13
+ return cluster.fail_deploy_namespace!
14
+ end
15
+
16
+ cluster.start_deploying!
17
+
18
+ begin
19
+ UffizziCore::ControllerService.create_cluster(cluster)
20
+ rescue UffizziCore::ControllerClient::ConnectionError
21
+ return cluster.fail!
22
+ end
23
+
24
+ UffizziCore::Cluster::ManageDeployingJob.perform_in(5.seconds, cluster.id)
25
+ end
26
+
27
+ def manage_deploying(cluster, try)
28
+ return if cluster.disabled?
29
+ return cluster.fail! if try > Settings.vcluster.max_creation_retry_count
30
+
31
+ deployed_cluster = UffizziCore::ControllerService.show_cluster(cluster)
32
+
33
+ if deployed_cluster.status.ready && deployed_cluster.status.kube_config.present?
34
+ cluster.finish_deploy
35
+ cluster.kubeconfig = deployed_cluster.status.kube_config
36
+ cluster.save!
37
+
38
+ return
39
+ end
40
+
41
+ UffizziCore::Cluster::ManageDeployingJob.perform_in(5.seconds, cluster.id, ++try)
42
+ end
43
+ end
44
+ end
@@ -9,16 +9,7 @@ class UffizziCore::ControllerService
9
9
  config_file: UffizziCore::Controller::ApplyConfigFile::ConfigFileSerializer.new(config_file).as_json,
10
10
  }
11
11
 
12
- controller_client.apply_config_file(deployment_id: deployment.id, config_file_id: config_file.id, body: body)
13
- end
14
-
15
- def create_deployment(deployment)
16
- body = UffizziCore::Controller::CreateDeployment::DeploymentSerializer.new(deployment).as_json
17
- controller_client.create_deployment(deployment_id: deployment.id, body: body)
18
- end
19
-
20
- def delete_deployment(deployment_id)
21
- controller_client.delete_deployment(deployment_id: deployment_id)
12
+ controller_client(deployment).apply_config_file(deployment_id: deployment.id, config_file_id: config_file.id, body: body)
22
13
  end
23
14
 
24
15
  def apply_credential(deployment, credential)
@@ -29,11 +20,11 @@ class UffizziCore::ControllerService
29
20
  options = { image: image }
30
21
 
31
22
  body = UffizziCore::Controller::CreateCredential::CredentialSerializer.new(credential, options).as_json
32
- controller_client.apply_credential(deployment_id: deployment.id, body: body)
23
+ controller_client(deployment).apply_credential(deployment_id: deployment.id, body: body)
33
24
  end
34
25
 
35
26
  def delete_credential(deployment, credential)
36
- controller_client.delete_credential(deployment_id: deployment.id, credential_id: credential.id)
27
+ controller_client(deployment).delete_credential(deployment_id: deployment.id, credential_id: credential.id)
37
28
  end
38
29
 
39
30
  def deploy_containers(deployment, containers)
@@ -65,11 +56,11 @@ class UffizziCore::ControllerService
65
56
  body = password_protection_module.add_password_configuration(body, deployment.project_id)
66
57
  end
67
58
 
68
- controller_client.deploy_containers(deployment_id: deployment.id, body: body)
59
+ controller_client(deployment).deploy_containers(deployment_id: deployment.id, body: body)
69
60
  end
70
61
 
71
- def deployment_exists?(deployment)
72
- controller_client.deployment(deployment_id: deployment.id).code == 200
62
+ def namespace_exists?(deployable)
63
+ controller_client(deployable).namespace(namespace: deployable.namespace).code == 200
73
64
  end
74
65
 
75
66
  def fetch_deployment_events(deployment)
@@ -77,22 +68,53 @@ class UffizziCore::ControllerService
77
68
  end
78
69
 
79
70
  def fetch_pods(deployment)
80
- pods = controller_client.deployment_containers(deployment_id: deployment.id).result || []
71
+ pods = controller_client(deployment).deployment_containers(deployment_id: deployment.id).result || []
81
72
  pods.filter { |pod| pod.metadata.name.start_with?(Settings.controller.namespace_prefix) }
82
73
  end
83
74
 
84
- def fetch_namespace(deployment)
85
- controller_client.deployment(deployment_id: deployment.id).result || nil
75
+ def fetch_namespace(deployable)
76
+ controller_client(deployable).namespace(namespace: deployable.namespace).result || nil
77
+ end
78
+
79
+ def create_namespace(deployable)
80
+ body = { namespace: deployable.namespace }
81
+ controller_client(deployable).create_namespace(body: body).result || nil
82
+ end
83
+
84
+ def delete_namespace(deployable)
85
+ controller_client(deployable).delete_namespace(namespace: deployable.namespace)
86
+ end
87
+
88
+ def create_cluster(cluster)
89
+ body = UffizziCore::Controller::CreateCluster::ClusterSerializer.new(cluster).as_json
90
+ controller_client(cluster).create_cluster(namespace: cluster.namespace, body: body).result
91
+ end
92
+
93
+ def show_cluster(cluster)
94
+ controller_client(cluster).show_cluster(namespace: cluster.namespace, name: cluster.name).result
95
+ end
96
+
97
+ def delete_cluster(cluster)
98
+ controller_client(cluster).delete_cluster(namespace: cluster.namespace)
86
99
  end
87
100
 
88
101
  private
89
102
 
90
103
  def request_events(deployment)
91
- controller_client.deployment_containers_events(deployment_id: deployment.id)
104
+ controller_client(deployment).deployment_containers_events(deployment_id: deployment.id)
92
105
  end
93
106
 
94
- def controller_client
95
- UffizziCore::ControllerClient.new
107
+ def controller_client(deployable)
108
+ settings = case deployable
109
+ when UffizziCore::Deployment
110
+ Settings.controller
111
+ when UffizziCore::Cluster
112
+ Settings.vcluster_controller
113
+ else
114
+ raise StandardError, "Deployable #{deployable.class.name} undefined"
115
+ end
116
+
117
+ UffizziCore::ControllerClient.new(settings)
96
118
  end
97
119
  end
98
120
  end
@@ -37,7 +37,7 @@ class UffizziCore::LogsService
37
37
  end
38
38
 
39
39
  def controller_client
40
- UffizziCore::ControllerClient.new
40
+ UffizziCore::ControllerClient.new(Settings.controller)
41
41
  end
42
42
  end
43
43
  end
data/config/routes.rb CHANGED
@@ -10,6 +10,7 @@ UffizziCore::Engine.routes.draw do
10
10
  resources :projects, only: ['index', 'show', 'destroy'], param: :slug do
11
11
  scope module: :projects do
12
12
  resource :compose_file, only: ['show', 'create', 'destroy']
13
+ resources :clusters, only: [:index, :create, :show, :destroy], param: :name
13
14
  resources :deployments, only: ['index', 'show', 'create', 'destroy', 'update'] do
14
15
  post :deploy_containers, on: :member
15
16
  scope module: :deployments do
@@ -41,9 +42,11 @@ UffizziCore::Engine.routes.draw do
41
42
  resource :session, only: ['create']
42
43
  end
43
44
 
44
- resources :accounts, only: [] do
45
+ resources :accounts, only: ['show'], param: :name
46
+
47
+ resources :accounts, only: ['index'] do
45
48
  scope module: :accounts do
46
- resources :projects, only: ['create']
49
+ resources :projects, only: ['index', 'create']
47
50
  resources :credentials, only: ['index', 'create', 'update', 'destroy'], param: :type do
48
51
  member do
49
52
  get :check_credential
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateClusters < ActiveRecord::Migration[6.1]
4
+ def change
5
+ create_table('uffizzi_core_clusters', force: :cascade) do |t|
6
+ t.references :project, null: false,
7
+ foreign_key: true,
8
+ index: { name: :index_cluster_on_project_id },
9
+ foreign_key: { to_table: :uffizzi_core_projects }
10
+ t.bigint 'deployed_by_id', foreign_key: true
11
+ t.string 'state'
12
+ t.string 'name'
13
+ t.text 'manifest'
14
+ t.text 'kubeconfig'
15
+
16
+ t.timestamps
17
+ end
18
+ end
19
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UffizziCore
4
- VERSION = '2.1.29'
4
+ VERSION = '2.2.0'
5
5
  end
data/lib/uffizzi_core.rb CHANGED
@@ -35,6 +35,7 @@ module UffizziCore
35
35
  accounts: :uffizzi_core_accounts,
36
36
  activity_items: :uffizzi_core_activity_items,
37
37
  builds: :uffizzi_core_builds,
38
+ clusters: :uffizzi_core_clusters,
38
39
  comments: :uffizzi_core_comments,
39
40
  compose_files: :uffizzi_core_compose_files,
40
41
  config_files: :uffizzi_core_config_files,
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: 2.1.29
4
+ version: 2.2.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: 2023-06-21 00:00:00.000000000 Z
12
+ date: 2023-07-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aasm
@@ -762,6 +762,7 @@ files:
762
762
  - app/contexts/uffizzi_core/base_context.rb
763
763
  - app/contexts/uffizzi_core/project_context.rb
764
764
  - app/contexts/uffizzi_core/webhooks_context.rb
765
+ - app/controller_modules/uffizzi_core/api/cli/v1/projects/clusters_controller_module.rb
765
766
  - app/controller_modules/uffizzi_core/api/cli/v1/projects/deployments_controller_module.rb
766
767
  - app/controller_modules/uffizzi_core/api/cli/v1/projects_controller_module.rb
767
768
  - app/controllers/concerns/uffizzi_core/auth_management.rb
@@ -770,10 +771,12 @@ files:
770
771
  - app/controllers/uffizzi_core/api/cli/v1/accounts/application_controller.rb
771
772
  - app/controllers/uffizzi_core/api/cli/v1/accounts/credentials_controller.rb
772
773
  - app/controllers/uffizzi_core/api/cli/v1/accounts/projects_controller.rb
774
+ - app/controllers/uffizzi_core/api/cli/v1/accounts_controller.rb
773
775
  - app/controllers/uffizzi_core/api/cli/v1/application_controller.rb
774
776
  - app/controllers/uffizzi_core/api/cli/v1/ci/application_controller.rb
775
777
  - app/controllers/uffizzi_core/api/cli/v1/ci/sessions_controller.rb
776
778
  - app/controllers/uffizzi_core/api/cli/v1/projects/application_controller.rb
779
+ - app/controllers/uffizzi_core/api/cli/v1/projects/clusters_controller.rb
777
780
  - app/controllers/uffizzi_core/api/cli/v1/projects/compose_files_controller.rb
778
781
  - app/controllers/uffizzi_core/api/cli/v1/projects/deployments/activity_items_controller.rb
779
782
  - app/controllers/uffizzi_core/api/cli/v1/projects/deployments/application_controller.rb
@@ -798,6 +801,7 @@ files:
798
801
  - app/forms/uffizzi_core/api/cli/v1/account/credential/check_credential_form.rb
799
802
  - app/forms/uffizzi_core/api/cli/v1/account/credential/create_form.rb
800
803
  - app/forms/uffizzi_core/api/cli/v1/account/credential/update_form.rb
804
+ - app/forms/uffizzi_core/api/cli/v1/cluster/create_form.rb
801
805
  - app/forms/uffizzi_core/api/cli/v1/compose_file/check_credentials_form.rb
802
806
  - app/forms/uffizzi_core/api/cli/v1/compose_file/cli_form.rb
803
807
  - app/forms/uffizzi_core/api/cli/v1/compose_file/create_form.rb
@@ -819,6 +823,9 @@ files:
819
823
  - app/jobs/uffizzi_core/account/update_credential_job.rb
820
824
  - app/jobs/uffizzi_core/activity_item/docker/update_digest_job.rb
821
825
  - app/jobs/uffizzi_core/application_job.rb
826
+ - app/jobs/uffizzi_core/cluster/delete_job.rb
827
+ - app/jobs/uffizzi_core/cluster/deploy_job.rb
828
+ - app/jobs/uffizzi_core/cluster/manage_deploying_job.rb
822
829
  - app/jobs/uffizzi_core/config_file/apply_job.rb
823
830
  - app/jobs/uffizzi_core/deployment/create_credential_job.rb
824
831
  - app/jobs/uffizzi_core/deployment/create_credentials_job.rb
@@ -830,6 +837,7 @@ files:
830
837
  - app/jobs/uffizzi_core/deployment/update_credential_job.rb
831
838
  - app/lib/uffizzi_core/concerns/models/account.rb
832
839
  - app/lib/uffizzi_core/concerns/models/activity_item.rb
840
+ - app/lib/uffizzi_core/concerns/models/cluster.rb
833
841
  - app/lib/uffizzi_core/concerns/models/comment.rb
834
842
  - app/lib/uffizzi_core/concerns/models/compose_file.rb
835
843
  - app/lib/uffizzi_core/concerns/models/config_file.rb
@@ -863,6 +871,7 @@ files:
863
871
  - app/models/uffizzi_core/activity_item/github.rb
864
872
  - app/models/uffizzi_core/activity_item/memory_limit.rb
865
873
  - app/models/uffizzi_core/application_record.rb
874
+ - app/models/uffizzi_core/cluster.rb
866
875
  - app/models/uffizzi_core/comment.rb
867
876
  - app/models/uffizzi_core/compose_file.rb
868
877
  - app/models/uffizzi_core/config_file.rb
@@ -903,6 +912,8 @@ files:
903
912
  - app/models/uffizzi_core/user_project.rb
904
913
  - app/policies/uffizzi_core/api/cli/v1/accounts/credentials_policy.rb
905
914
  - app/policies/uffizzi_core/api/cli/v1/accounts/projects_policy.rb
915
+ - app/policies/uffizzi_core/api/cli/v1/accounts_policy.rb
916
+ - app/policies/uffizzi_core/api/cli/v1/projects/clusters_policy.rb
906
917
  - app/policies/uffizzi_core/api/cli/v1/projects/compose_files_policy.rb
907
918
  - app/policies/uffizzi_core/api/cli/v1/projects/deployments/activity_items_policy.rb
908
919
  - app/policies/uffizzi_core/api/cli/v1/projects/deployments/containers_policy.rb
@@ -914,6 +925,7 @@ files:
914
925
  - app/repositories/uffizzi_core/account_repo.rb
915
926
  - app/repositories/uffizzi_core/activity_item_repo.rb
916
927
  - app/repositories/uffizzi_core/basic_order_repo.rb
928
+ - app/repositories/uffizzi_core/cluster_repo.rb
917
929
  - app/repositories/uffizzi_core/comment_repo.rb
918
930
  - app/repositories/uffizzi_core/compose_file_repo.rb
919
931
  - app/repositories/uffizzi_core/config_file_repo.rb
@@ -931,12 +943,14 @@ files:
931
943
  - app/repositories/uffizzi_core/usage_repo.rb
932
944
  - app/repositories/uffizzi_core/user_repo.rb
933
945
  - app/responders/uffizzi_core/json_responder.rb
946
+ - app/serializers/uffizzi_core/api/cli/v1/account_serializer.rb
934
947
  - app/serializers/uffizzi_core/api/cli/v1/accounts/credential_serializer.rb
935
948
  - app/serializers/uffizzi_core/api/cli/v1/accounts/project_serializer.rb
936
949
  - app/serializers/uffizzi_core/api/cli/v1/project_serializer.rb
937
950
  - app/serializers/uffizzi_core/api/cli/v1/project_serializer/account_serializer.rb
938
951
  - app/serializers/uffizzi_core/api/cli/v1/project_serializer/compose_file_serializer.rb
939
952
  - app/serializers/uffizzi_core/api/cli/v1/project_serializer/deployment_serializer.rb
953
+ - app/serializers/uffizzi_core/api/cli/v1/projects/cluster_serializer.rb
940
954
  - app/serializers/uffizzi_core/api/cli/v1/projects/compose_file_serializer.rb
941
955
  - app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer.rb
942
956
  - app/serializers/uffizzi_core/api/cli/v1/projects/deployment_serializer/container_serializer.rb
@@ -953,6 +967,7 @@ files:
953
967
  - app/serializers/uffizzi_core/api/cli/v1/user_serializer/account_serializer.rb
954
968
  - app/serializers/uffizzi_core/base_serializer.rb
955
969
  - app/serializers/uffizzi_core/controller/apply_config_file/config_file_serializer.rb
970
+ - app/serializers/uffizzi_core/controller/create_cluster/cluster_serializer.rb
956
971
  - app/serializers/uffizzi_core/controller/create_credential/credential_serializer.rb
957
972
  - app/serializers/uffizzi_core/controller/create_deployment/deployment_serializer.rb
958
973
  - app/serializers/uffizzi_core/controller/deploy_containers/compose_file_serializer.rb
@@ -964,6 +979,7 @@ files:
964
979
  - app/serializers/uffizzi_core/controller/deploy_containers/host_volume_file_serializer.rb
965
980
  - app/services/uffizzi_core/account_service.rb
966
981
  - app/services/uffizzi_core/activity_item_service.rb
982
+ - app/services/uffizzi_core/cluster_service.rb
967
983
  - app/services/uffizzi_core/compose_file/builders/container_builder_service.rb
968
984
  - app/services/uffizzi_core/compose_file/builders/container_config_files_builder_service.rb
969
985
  - app/services/uffizzi_core/compose_file/builders/container_host_volume_files_builder_service.rb
@@ -1043,6 +1059,7 @@ files:
1043
1059
  - db/migrate/20230111000000_add_state_to_memberships.rb
1044
1060
  - db/migrate/20230306142513_add_last_deploy_at_to_deployments.rb
1045
1061
  - db/migrate/20230406154451_add_full_image_name_to_container.rb
1062
+ - db/migrate/20230613101901_create_clusters.rb
1046
1063
  - db/seeds.rb
1047
1064
  - lib/tasks/uffizzi_core_tasks.rake
1048
1065
  - lib/uffizzi_core.rb