uffizzi_core 2.2.24 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
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