uffizzi_core 2.2.24 → 2.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: 2d8112c651358e76a331ea2e85d1562ecd15724adaf89eeebc10146958ef9550
4
- data.tar.gz: fbc115075ccb252fbc6446b51fccd93c8571d3b106a83376b173b63c7de25fd7
3
+ metadata.gz: 065d0c7c4ecaca71f48ed176e3a433a379908220e7923be4604165d2d6760fe7
4
+ data.tar.gz: e79876e64df2b71896f0a34e40a7c1caf06b788914171a40e35b5feda33674d2
5
5
  SHA512:
6
- metadata.gz: 55f7fb96c94e8dfca56310456bc3202764d1e16173fe57e96c027609748ae10bd758af06c355682c2c8ef2634b10402ce899778efb7467a99f16bd5aa4745a7f
7
- data.tar.gz: 8d323b2dff07e8d9d03918e5642cd10093a9e2decc0f2cbf3a9e017b2ad99e8165bd447ea210a28a0497153b955244ebd68ce0d9d8db20424ed9cb05cfbf3273
6
+ metadata.gz: 1415234d2f29321aae904e5467a41108b44f5beb8291e6510a60dc58f72597a535e00a0a1a98d6208844717b7ca0b2c53fe6560a163983583a7ab60cc382d4dc
7
+ data.tar.gz: c1aea1bd41f011ad430ddd2c5af76d48518a2e4f099cb33615a4f81462a6cfee7777eeb80fef4607263cc403ed93580142db0166779002aa8272910da3d39f0d
@@ -78,6 +78,14 @@ class UffizziCore::ControllerClient
78
78
  get("/namespaces/#{namespace}/cluster/#{name}")
79
79
  end
80
80
 
81
+ def patch_cluster(name:, namespace:, body:)
82
+ patch("/namespaces/#{namespace}/cluster/#{name}", body)
83
+ end
84
+
85
+ def ingresses(namespace:)
86
+ get("/namespaces/#{namespace}/ingresses")
87
+ end
88
+
81
89
  private
82
90
 
83
91
  def get(url, params = {})
@@ -88,6 +96,10 @@ class UffizziCore::ControllerClient
88
96
  make_request(:post, url, params)
89
97
  end
90
98
 
99
+ def patch(url, params = {})
100
+ make_request(:patch, url, params)
101
+ end
102
+
91
103
  def make_request(method, url, params)
92
104
  response = connection.send(method, url, params)
93
105
  body = response.body
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Project::ClusterContext
4
+ attr_reader :user, :user_access_module, :project, :cluster, :params
5
+
6
+ def initialize(user, project, user_access_module, cluster, params)
7
+ @user = user
8
+ @user_access_module = user_access_module
9
+ @project = project
10
+ @cluster = cluster
11
+ @params = params
12
+ end
13
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Projects::Clusters::ApplicationController < UffizziCore::Api::Cli::V1::Projects::ApplicationController
4
+ def resource_cluster
5
+ @resource_cluster ||= if request_by_admin? || valid_request_from_ci_workflow?
6
+ active_project_clusters.find_by!(name: params[:cluster_name])
7
+ else
8
+ active_project_clusters.deployed_by_user(current_user).find_by!(name: params[:cluster_name])
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def active_project_clusters
15
+ @active_project_clusters ||= resource_project.clusters.enabled
16
+ end
17
+
18
+ def request_by_admin?
19
+ current_user.admin_access_to_project?(resource_project)
20
+ end
21
+
22
+ def valid_request_from_ci_workflow?
23
+ ci_module.valid_request_from_ci_workflow?(params)
24
+ end
25
+
26
+ def policy_context
27
+ UffizziCore::Project::ClusterContext.new(current_user, resource_project, user_access_module, resource_cluster, params)
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Projects::Clusters::IngressesController <
4
+ UffizziCore::Api::Cli::V1::Projects::Clusters::ApplicationController
5
+ before_action :authorize_uffizzi_core_api_cli_v1_projects_clusters_ingresses
6
+
7
+ def index
8
+ hosts = UffizziCore::ControllerService.ingress_hosts(resource_cluster)
9
+ user_hosts = UffizziCore::ClusterService.filter_user_ingress_host(resource_cluster, hosts)
10
+
11
+ data = {
12
+ ingresses: user_hosts,
13
+ }
14
+
15
+ respond_with data
16
+ end
17
+ end
@@ -30,6 +30,32 @@ class UffizziCore::Api::Cli::V1::Projects::ClustersController < UffizziCore::Api
30
30
  respond_with cluster_form
31
31
  end
32
32
 
33
+ def scale_down
34
+ if resource_cluster.deployed?
35
+ UffizziCore::ClusterService.scale_down!(resource_cluster)
36
+ return respond_with resource_cluster
37
+ end
38
+
39
+ return render_scale_error(I18n.t('cluster.already_asleep', name: resource_cluster.name)) if resource_cluster.scaled_down?
40
+
41
+ if resource_cluster.deploying_namespace? || resource_cluster.deploying?
42
+ render_scale_error(I18n.t('cluster.deploy_in_process', name: resource_cluster.name))
43
+ end
44
+ rescue AASM::InvalidTransition, UffizziCore::ClusterScaleError => e
45
+ render_scale_error(e.message)
46
+ end
47
+
48
+ def scale_up
49
+ if resource_cluster.scaled_down?
50
+ UffizziCore::ClusterService.scale_up!(resource_cluster)
51
+ return respond_with resource_cluster
52
+ end
53
+
54
+ return render_scale_error(I18n.t('cluster.already_awake', name: resource_cluster.name)) if resource_cluster.deployed?
55
+ rescue AASM::InvalidTransition, UffizziCore::ClusterScaleError => e
56
+ render_scale_error(e.message)
57
+ end
58
+
33
59
  def show
34
60
  respond_with resource_cluster
35
61
  end
@@ -63,6 +89,10 @@ class UffizziCore::Api::Cli::V1::Projects::ClustersController < UffizziCore::Api
63
89
  params.require(:cluster)
64
90
  end
65
91
 
92
+ def render_scale_error(message)
93
+ render json: { errors: { state: [message] } }, status: :unprocessable_entity
94
+ end
95
+
66
96
  def find_kubernetes_distribution(version)
67
97
  return UffizziCore::KubernetesDistribution.default if version.blank?
68
98
 
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::ClusterScaleError < StandardError
4
+ def initialize(action)
5
+ message = I18n.t('cluster.scaling_failed', action: action)
6
+
7
+ super(message)
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Cluster::ManageScalingDownJob < UffizziCore::ApplicationJob
4
+ sidekiq_options queue: :clusters, retry: Settings.default_job_retry_count
5
+
6
+ def perform(id)
7
+ cluster = UffizziCore::Cluster.find(id)
8
+
9
+ UffizziCore::ClusterService.manage_scale_down(cluster)
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Cluster::ManageScalingUpJob < UffizziCore::ApplicationJob
4
+ sidekiq_options queue: :clusters, retry: Settings.default_job_retry_count
5
+
6
+ def perform(id, try = 1)
7
+ cluster = UffizziCore::Cluster.find(id)
8
+
9
+ UffizziCore::ClusterService.manage_scale_up(cluster, try)
10
+ end
11
+ end
@@ -6,6 +6,7 @@ module UffizziCore::Concerns::Models::Cluster
6
6
 
7
7
  NAMESPACE_PREFIX = 'c'
8
8
 
9
+ # rubocop:disable Metrics/BlockLength
9
10
  included do
10
11
  include AASM
11
12
  extend Enumerize
@@ -27,6 +28,10 @@ module UffizziCore::Concerns::Models::Cluster
27
28
  state :failed_deploy_namespace
28
29
  state :deploying
29
30
  state :deployed
31
+ state :scaling_down
32
+ state :scaled_down
33
+ state :scaling_up
34
+ state :failed_scale_up
30
35
  state :failed
31
36
  state :disabled
32
37
 
@@ -42,12 +47,40 @@ module UffizziCore::Concerns::Models::Cluster
42
47
  transitions from: [:deploying], to: :deployed
43
48
  end
44
49
 
50
+ event :start_scaling_down do
51
+ transitions from: [:deployed], to: :scaling_down
52
+ end
53
+
54
+ event :scale_down do
55
+ transitions from: [:scaling_down], to: :scaled_down
56
+ end
57
+
58
+ event :start_scaling_up do
59
+ transitions from: [:scaled_down, :failed_scale_up], to: :scaling_up
60
+ end
61
+
62
+ event :scale_up do
63
+ transitions from: [:scaling_up], to: :deployed
64
+ end
65
+
66
+ event :fail_scale_up do
67
+ transitions from: [:scaling_up], to: :failed_scale_up
68
+ end
69
+
45
70
  event :fail do
46
71
  transitions from: [:deploying], to: :failed
47
72
  end
48
73
 
49
74
  event :disable, after: :after_disable do
50
- transitions from: [:deploying_namespace, :failed_deploy_namespace, :deploying, :deployed, :failed], to: :disabled
75
+ transitions from: [
76
+ :deploying_namespace,
77
+ :failed_deploy_namespace,
78
+ :deploying,
79
+ :deployed,
80
+ :scaling_down,
81
+ :scaled_down,
82
+ :failed,
83
+ ], to: :disabled
51
84
  end
52
85
  end
53
86
 
@@ -59,4 +92,5 @@ module UffizziCore::Concerns::Models::Cluster
59
92
  [NAMESPACE_PREFIX, id].join
60
93
  end
61
94
  end
95
+ # rubocop:enable Metrics/BlockLength
62
96
  end
@@ -21,5 +21,9 @@ module UffizziCore::Rbac::UserAccessService
21
21
  def any_access_to_project?(_user, _project)
22
22
  true
23
23
  end
24
+
25
+ def admin_access_to_project?(_user, _project)
26
+ true
27
+ end
24
28
  end
25
29
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Api::Cli::V1::Projects::Clusters::IngressesPolicy < UffizziCore::ApplicationPolicy
4
+ def index?
5
+ return true if context.user_access_module.admin_access_to_project?(context.user, context.project)
6
+
7
+ context.cluster.deployed_by_id == context.user.id
8
+ end
9
+ end
@@ -13,6 +13,14 @@ class UffizziCore::Api::Cli::V1::Projects::ClustersPolicy < UffizziCore::Applica
13
13
  context.user_access_module.admin_or_developer_access_to_project?(context.user, context.project)
14
14
  end
15
15
 
16
+ def scale_down?
17
+ context.user_access_module.admin_or_developer_access_to_project?(context.user, context.project)
18
+ end
19
+
20
+ def scale_up?
21
+ context.user_access_module.admin_or_developer_access_to_project?(context.user, context.project)
22
+ end
23
+
16
24
  def destroy?
17
25
  context.user_access_module.admin_or_developer_access_to_project?(context.user, context.project)
18
26
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UffizziCore::Controller::UpdateCluster::ClusterSerializer < UffizziCore::BaseSerializer
4
+ include UffizziCore::DependencyInjectionConcern
5
+ include_module_if_exists('UffizziCore::Controller::UpdateCluster::ClusterSerializerModule')
6
+
7
+ attributes :name, :manifest, :base_ingress_host
8
+
9
+ def base_ingress_host
10
+ managed_dns_zone = controller_settings_service.vcluster(object).managed_dns_zone
11
+
12
+ [object.namespace, managed_dns_zone].join('.')
13
+ end
14
+ end
@@ -24,6 +24,32 @@ class UffizziCore::ClusterService
24
24
  UffizziCore::Cluster::ManageDeployingJob.perform_in(5.seconds, cluster.id)
25
25
  end
26
26
 
27
+ def scale_up!(cluster)
28
+ cluster.start_scaling_up!
29
+ UffizziCore::ControllerService.patch_cluster(cluster, sleep: false)
30
+ UffizziCore::Cluster::ManageScalingUpJob.perform_in(5.seconds, cluster.id)
31
+ end
32
+
33
+ def manage_scale_up(cluster, try)
34
+ return cluster.fail_scale_up! if try > Settings.vcluster.max_scale_up_retry_count
35
+ return cluster.scale_up! if ready?(cluster)
36
+
37
+ UffizziCore::Cluster::ManageScalingUpJob.perform_in(5.seconds, cluster.id, ++try)
38
+ end
39
+
40
+ def scale_down!(cluster)
41
+ cluster.start_scaling_down!
42
+ UffizziCore::ControllerService.patch_cluster(cluster, sleep: true)
43
+
44
+ UffizziCore::Cluster::ManageScalingDownJob.perform_in(5.seconds, cluster.id)
45
+ end
46
+
47
+ def manage_scale_down(cluster)
48
+ return cluster.scale_down! unless awake?(cluster)
49
+
50
+ UffizziCore::Cluster::ManageScalingDownJob.perform_in(5.seconds, cluster.id)
51
+ end
52
+
27
53
  def manage_deploying(cluster, try)
28
54
  return if cluster.disabled?
29
55
  return cluster.fail! if try > Settings.vcluster.max_creation_retry_count
@@ -41,5 +67,23 @@ class UffizziCore::ClusterService
41
67
 
42
68
  UffizziCore::Cluster::ManageDeployingJob.perform_in(5.seconds, cluster.id, ++try)
43
69
  end
70
+
71
+ def filter_user_ingress_host(cluster, ingress_hosts)
72
+ ingress_hosts.reject { |h| h == cluster.host }
73
+ end
74
+
75
+ private
76
+
77
+ def awake?(cluster)
78
+ data = UffizziCore::ControllerService.show_cluster(cluster)
79
+
80
+ !data.status.sleep
81
+ end
82
+
83
+ def ready?(cluster)
84
+ data = UffizziCore::ControllerService.show_cluster(cluster)
85
+
86
+ data.status.ready
87
+ end
44
88
  end
45
89
  end
@@ -110,6 +110,23 @@ class UffizziCore::ControllerService
110
110
  controller_client(cluster).delete_cluster(namespace: cluster.namespace)
111
111
  end
112
112
 
113
+ def patch_cluster(cluster, sleep:)
114
+ body = UffizziCore::Controller::UpdateCluster::ClusterSerializer.new(cluster).as_json
115
+ body[:sleep] = sleep
116
+
117
+ controller_client(cluster).patch_cluster(name: cluster.name, namespace: cluster.namespace, body: body)
118
+ end
119
+
120
+ def ingress_hosts(cluster)
121
+ namespace = cluster.namespace
122
+
123
+ ingresses = controller_client(cluster).ingresses(namespace: namespace).result.items
124
+
125
+ ingresses.map do |ingress|
126
+ ingress.spec.rules.map(&:host)
127
+ end.flatten
128
+ end
129
+
113
130
  private
114
131
 
115
132
  def check_any_container_has_public_port(containers)
@@ -83,6 +83,12 @@ en:
83
83
  deployment:
84
84
  invalid_state: Preview with ID deployment-%{id} %{state}
85
85
  already_exists: An active deployment already exists
86
+
87
+ cluster:
88
+ already_asleep: The cluster %{name} is already asleep.
89
+ already_awake: The cluster %{name} is already awake.
90
+ scaling_failed: Failed to %{action} cluster.
91
+ deploy_in_process: Please wait until the cluster %{name} is deployed.
86
92
 
87
93
  session:
88
94
  unsupported_login_type: This type of login is not supported
data/config/routes.rb CHANGED
@@ -10,7 +10,15 @@ 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
+ resources :clusters, only: [:index, :create, :show, :destroy], param: :name do
14
+ member do
15
+ put :scale_down
16
+ put :scale_up
17
+ end
18
+ scope module: :clusters do
19
+ resources :ingresses, only: ['index']
20
+ end
21
+ end
14
22
  resources :deployments, only: ['index', 'show', 'create', 'destroy', 'update'] do
15
23
  post :deploy_containers, on: :member
16
24
  scope module: :deployments do
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module UffizziCore
4
- VERSION = '2.2.24'
4
+ VERSION = '2.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: 2.2.24
4
+ version: 2.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: 2023-10-16 00:00:00.000000000 Z
12
+ date: 2023-10-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aasm
@@ -774,6 +774,7 @@ files:
774
774
  - app/clients/uffizzi_core/google_registry_client/request_result.rb
775
775
  - app/contexts/uffizzi_core/account_context.rb
776
776
  - app/contexts/uffizzi_core/base_context.rb
777
+ - app/contexts/uffizzi_core/project/cluster_context.rb
777
778
  - app/contexts/uffizzi_core/project_context.rb
778
779
  - app/contexts/uffizzi_core/webhooks_context.rb
779
780
  - app/controller_modules/uffizzi_core/api/cli/v1/projects/clusters_controller_module.rb
@@ -791,6 +792,8 @@ files:
791
792
  - app/controllers/uffizzi_core/api/cli/v1/ci/application_controller.rb
792
793
  - app/controllers/uffizzi_core/api/cli/v1/ci/sessions_controller.rb
793
794
  - app/controllers/uffizzi_core/api/cli/v1/projects/application_controller.rb
795
+ - app/controllers/uffizzi_core/api/cli/v1/projects/clusters/application_controller.rb
796
+ - app/controllers/uffizzi_core/api/cli/v1/projects/clusters/ingresses_controller.rb
794
797
  - app/controllers/uffizzi_core/api/cli/v1/projects/clusters_controller.rb
795
798
  - app/controllers/uffizzi_core/api/cli/v1/projects/compose_files_controller.rb
796
799
  - app/controllers/uffizzi_core/api/cli/v1/projects/deployments/activity_items_controller.rb
@@ -804,6 +807,7 @@ files:
804
807
  - app/controllers/uffizzi_core/api/cli/v1/projects_controller.rb
805
808
  - app/controllers/uffizzi_core/api/cli/v1/sessions_controller.rb
806
809
  - app/controllers/uffizzi_core/application_controller.rb
810
+ - app/errors/uffizzi_core/cluster_scale_error.rb
807
811
  - app/errors/uffizzi_core/compose_file/build_error.rb
808
812
  - app/errors/uffizzi_core/compose_file/credential_error.rb
809
813
  - app/errors/uffizzi_core/compose_file/parse_error.rb
@@ -841,6 +845,8 @@ files:
841
845
  - app/jobs/uffizzi_core/cluster/delete_job.rb
842
846
  - app/jobs/uffizzi_core/cluster/deploy_job.rb
843
847
  - app/jobs/uffizzi_core/cluster/manage_deploying_job.rb
848
+ - app/jobs/uffizzi_core/cluster/manage_scaling_down_job.rb
849
+ - app/jobs/uffizzi_core/cluster/manage_scaling_up_job.rb
844
850
  - app/jobs/uffizzi_core/config_file/apply_job.rb
845
851
  - app/jobs/uffizzi_core/deployment/create_credential_job.rb
846
852
  - app/jobs/uffizzi_core/deployment/create_credentials_job.rb
@@ -933,6 +939,7 @@ files:
933
939
  - app/policies/uffizzi_core/api/cli/v1/accounts/credentials_policy.rb
934
940
  - app/policies/uffizzi_core/api/cli/v1/accounts/projects_policy.rb
935
941
  - app/policies/uffizzi_core/api/cli/v1/accounts_policy.rb
942
+ - app/policies/uffizzi_core/api/cli/v1/projects/clusters/ingresses_policy.rb
936
943
  - app/policies/uffizzi_core/api/cli/v1/projects/clusters_policy.rb
937
944
  - app/policies/uffizzi_core/api/cli/v1/projects/compose_files_policy.rb
938
945
  - app/policies/uffizzi_core/api/cli/v1/projects/deployments/activity_items_policy.rb
@@ -1000,6 +1007,7 @@ files:
1000
1007
  - app/serializers/uffizzi_core/controller/deploy_containers/container_serializer/container_host_volume_file_serializer.rb
1001
1008
  - app/serializers/uffizzi_core/controller/deploy_containers/credential_serializer.rb
1002
1009
  - app/serializers/uffizzi_core/controller/deploy_containers/host_volume_file_serializer.rb
1010
+ - app/serializers/uffizzi_core/controller/update_cluster/cluster_serializer.rb
1003
1011
  - app/services/uffizzi_core/account_service.rb
1004
1012
  - app/services/uffizzi_core/activity_item_service.rb
1005
1013
  - app/services/uffizzi_core/ci_service.rb