Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
moklidia committed Jul 31, 2023
2 parents deabccb + d98e3fc commit 57d76ec
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 38 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
uffizzi-cli (2.0.11)
uffizzi-cli (2.0.12)
activesupport
awesome_print
faker
Expand Down
1 change: 1 addition & 0 deletions config/uffizzi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ def self.configure
docker_registry: 'UffizziCore::Credential::DockerRegistry',
}
config.default_server = 'app.uffizzi.com'
config.default_kubeconfig_path = ENV['CLI_DEFAULT_KUBECONFIG_PATH'] || '~/.kube/config'
end
end
77 changes: 62 additions & 15 deletions lib/uffizzi/cli/cluster.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
require 'faker'
require 'uffizzi'
require 'uffizzi/auth_helper'
require 'uffizzi/helpers/config_helper'
require 'uffizzi/services/preview_service'
require 'uffizzi/services/command_service'
require 'uffizzi/services/cluster_service'
Expand Down Expand Up @@ -37,6 +38,7 @@ def describe(name)
end

desc 'delete [NAME]', 'Delete a cluster'
method_option :'delete-config', required: false, type: :boolean, aliases: '-dc'
def delete(name)
run('delete', cluster_name: name)
end
Expand Down Expand Up @@ -122,17 +124,45 @@ def handle_create_command(project_slug)
end

def handle_describe_command(project_slug, command_args)
response = get_cluster(ConfigFile.read_option(:server), project_slug, command_args[:cluster_name])
cluster_data = fetch_cluster_data(project_slug, command_args[:cluster_name])

if ResponseHelper.ok?(response)
handle_succeed_describe_response(response)
else
ResponseHelper.handle_failed_response(response)
end
handle_succeed_describe(cluster_data)
end

def handle_delete_command(project_slug, command_args)
cluster_name = command_args[:cluster_name]
is_delete_kubeconfig = options[:'delete-config']

return handle_delete_cluster(project_slug, cluster_name) unless is_delete_kubeconfig

cluster_data = fetch_cluster_data(project_slug, cluster_name)
kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])

handle_delete_cluster(project_slug, cluster_name)
exclude_kubeconfig(cluster_data[:id], kubeconfig)
end

def exclude_kubeconfig(cluster_id, kubeconfig)
cluster_config = Uffizzi::ConfigHelper.cluster_config_by_id(cluster_id)
return if cluster_config.nil?

kubeconfig_path = cluster_config[:kubeconfig_path]
ConfigFile.write_option(:clusters, Uffizzi::ConfigHelper.clusters_config_without(cluster_id))

KubeconfigService.save_to_filepath(kubeconfig_path, kubeconfig) do |kubeconfig_by_path|
if kubeconfig_by_path.nil?
msg = "Warning: kubeconfig at path #{kubeconfig_path} does not exist"
return Uffizzi.ui.say(msg)
end

new_kubeconfig = KubeconfigService.exclude(kubeconfig_by_path, kubeconfig)
first_context = KubeconfigService.get_first_context(new_kubeconfig)
new_current_context = first_context.present? ? first_context['name'] : nil
KubeconfigService.update_current_context(new_kubeconfig, new_current_context)
end
end

def handle_delete_cluster(project_slug, cluster_name)
response = delete_cluster(ConfigFile.read_option(:server), project_slug, cluster_name)

if ResponseHelper.no_content?(response)
Expand All @@ -145,12 +175,9 @@ def handle_delete_command(project_slug, command_args)
def handle_update_kubeconfig_command(project_slug, command_args)
cluster_name = command_args[:cluster_name]
kubeconfig_path = options[:kubeconfig] || KubeconfigService.default_path
response = get_cluster(Uffizzi::ConfigFile.read_option(:server), project_slug, cluster_name)
return Uffizzi::ResponseHelper.handle_failed_response(response) unless Uffizzi::ResponseHelper.ok?(response)
cluster_data = fetch_cluster_data(project_slug, cluster_name)

cluster_data = response.dig(:body, :cluster)

if cluster_data[:kubeconfig].nil? || cluster_data[:kubeconfig].empty?
unless cluster_data[:kubeconfig].present?
say_error_update_kubeconfig(cluster_data)
end

Expand All @@ -164,6 +191,8 @@ def handle_update_kubeconfig_command(project_slug, command_args)
KubeconfigService.update_current_context(merged_kubeconfig, current_context)
end

update_clusters_config(cluster_data[:id], kubeconfig_path: kubeconfig_path)

return if options[:quiet]

Uffizzi.ui.say("Kubeconfig was updated by the path: #{kubeconfig_path}")
Expand Down Expand Up @@ -226,8 +255,7 @@ def handle_succeed_list_response(response)
Uffizzi.ui.say(clusters)
end

def handle_succeed_describe_response(response)
cluster_data = response[:body][:cluster]
def handle_succeed_describe(cluster_data)
prepared_cluster_data = {
name: cluster_data[:name],
status: cluster_data[:state],
Expand All @@ -245,18 +273,27 @@ def handle_succeed_describe_response(response)
end

def handle_succeed_create_response(cluster_data, kubeconfig_path)
kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
parsed_kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
rendered_cluster_data = render_cluster_data(cluster_data)

Uffizzi.ui.enable_stdout
Uffizzi.ui.say("Cluster with name: #{rendered_cluster_data[:name]} was created.")
Uffizzi.ui.say(rendered_cluster_data) if Uffizzi.ui.output_format

kubeconfig_path = kubeconfig_path.nil? ? KubeconfigService.default_path : kubeconfig_path
KubeconfigService.save_to_filepath(kubeconfig_path, kubeconfig)
KubeconfigService.save_to_filepath(kubeconfig_path, parsed_kubeconfig) do |kubeconfig_by_path|
KubeconfigService.merge(kubeconfig_by_path, parsed_kubeconfig)
end

update_clusters_config(cluster_data[:id], kubeconfig_path: kubeconfig_path)
GithubService.write_to_github_env(rendered_cluster_data) if GithubService.github_actions_exists?
end

def update_clusters_config(id, params)
clusters_config = Uffizzi::ConfigHelper.update_clusters_config_by_id(id, params)
ConfigFile.write_option(:clusters, clusters_config)
end

def render_cluster_data(cluster_data)
kubeconfig = parse_kubeconfig(cluster_data[:kubeconfig])
raise Uffizzi::Error.new('The kubeconfig data is empty') unless kubeconfig
Expand All @@ -270,5 +307,15 @@ def render_cluster_data(cluster_data)
def parse_kubeconfig(kubeconfig)
Psych.safe_load(Base64.decode64(kubeconfig))
end

def fetch_cluster_data(project_slug, cluster_name)
response = get_cluster(ConfigFile.read_option(:server), project_slug, cluster_name)

if ResponseHelper.ok?(response)
response.dig(:body, :cluster)
else
ResponseHelper.handle_failed_response(response)
end
end
end
end
34 changes: 34 additions & 0 deletions lib/uffizzi/helpers/config_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

module Uffizzi
module ConfigHelper
CLUSTER_PARAMS = [:kubeconfig_path].freeze

class ConfigParamsError < StandardError
def initialize(unavailable_params, key)
msg = "These params #{unavailable_params.join(', ')} is not available for #{key}"

super(msg)
end
end

class << self
def read_option_from_config(option)
ConfigFile.option_has_value?(option) ? ConfigFile.read_option(option) : nil
Expand All @@ -10,6 +20,30 @@ def read_option_from_config(option)
def account_config(id, name = nil)
{ id: id, name: name }
end

def update_clusters_config_by_id(id, params)
unavailable_params = params.keys - CLUSTER_PARAMS
raise ConfigParamsError.new(unavailable_params, :cluster) if unavailable_params.present?

current_cluster = cluster_config_by_id(id) || {}
new_current_cluster = current_cluster.merge({ id: id }).merge(params)

clusters_config_without(id) << new_current_cluster
end

def clusters_config_without(id)
clusters.reject { |c| c[:id] == id }
end

def cluster_config_by_id(id)
clusters.detect { |c| c[:id] == id }
end

private

def clusters
read_option_from_config(:clusters) || []
end
end
end
end
55 changes: 38 additions & 17 deletions lib/uffizzi/services/kubeconfig_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ def initialize(file_path)
end
end

DEFAULT_KUBECONFIG_PATH = '~/.kube/config'
KUBECONFIG_GENERAL_KEYS = ['apiVersion', 'clusters', 'contexts', 'current-context', 'kind', 'users'].freeze

class << self
Expand All @@ -24,16 +23,33 @@ def merge(target_kubeconfig, source_kubeconfig)
new_cluster_name = get_current_cluster_name(source_kubeconfig)

if cluster_exists_in_kubeconfig?(target_kubeconfig, new_cluster_name)
replace(target_kubeconfig, source_kubeconfig, new_cluster_name)
replace_by_cluster_name(target_kubeconfig, source_kubeconfig, new_cluster_name)
else
add(target_kubeconfig, source_kubeconfig)
end
end

def exclude(target_kubeconfig, source_kubeconfig)
return if target_kubeconfig.nil?

excludable_cluster_name = get_current_cluster_name(source_kubeconfig)
exclude_by_cluster_name(target_kubeconfig, excludable_cluster_name)
end

def get_current_context(kubeconfig)
kubeconfig['current-context']
end

def get_first_context(kubeconfig)
kubeconfig.fetch('contexts', [])[0]
end

def get_current_cluster_name(kubeconfig)
kubeconfig['contexts']
.detect { |c| c['name'] == get_current_context(kubeconfig) }
.dig('context', 'cluster')
end

def update_current_context(kubeconfig, current_context)
new_kubeconfig = kubeconfig.deep_dup
new_kubeconfig['current-context'] = current_context
Expand All @@ -49,21 +65,23 @@ def save_to_filepath(filepath, kubeconfig)
raise InvalidKubeconfigError.new(filepath)
end

new_kubeconfig = block_given? ? yield(target_kubeconfig) : merge(target_kubeconfig, kubeconfig)
new_kubeconfig = block_given? ? yield(target_kubeconfig) : kubeconfig
return if new_kubeconfig.nil?

dir_path = File.dirname(real_file_path)
FileUtils.mkdir_p(dir_path) unless File.directory?(dir_path)
File.write(real_file_path, new_kubeconfig.to_yaml)
end

def default_path
kubeconfig_env_path || DEFAULT_KUBECONFIG_PATH
kubeconfig_env_path || Uffizzi.configuration.default_kubeconfig_path
end

private

def cluster_exists_in_kubeconfig?(kubeconfig, cluster_name)
!kubeconfig['clusters'].detect { |c| c['name'] == cluster_name }.nil?
clusters = kubeconfig['clusters'] || []
clusters.detect { |c| c['name'] == cluster_name }.present?
end

def add(target_kubeconfig, source_kubeconfig)
Expand All @@ -75,22 +93,25 @@ def add(target_kubeconfig, source_kubeconfig)
new_kubeconfig
end

def replace(target_kubeconfig, source_kubeconfig, cluster_name)
new_kubeconfig = target_kubeconfig.deep_dup
new_kubeconfig['clusters'].delete_if { |c| c['name'] == cluster_name }
target_user = new_kubeconfig['contexts']
.detect { |c| c.dig('context', 'cluster') == cluster_name }
.dig('context', 'user')
new_kubeconfig['contexts'].delete_if { |c| c.dig('context', 'cluster') == cluster_name }
new_kubeconfig['users'].delete_if { |c| c['name'] == target_user }
def replace_by_cluster_name(target_kubeconfig, source_kubeconfig, cluster_name)
new_kubeconfig = exclude_by_cluster_name(target_kubeconfig, cluster_name)

add(new_kubeconfig, source_kubeconfig)
end

def get_current_cluster_name(kubeconfig)
kubeconfig['contexts']
.detect { |c| c['name'] == kubeconfig['current-context'] }
.dig('context', 'cluster')
def exclude_by_cluster_name(kubeconfig, cluster_name)
clusters = kubeconfig['clusters']
contexts = kubeconfig['contexts']
users = kubeconfig['users']

return kubeconfig if clusters.empty? || contexts.empty? || users.empty?

target_user = contexts.detect { |c| c.dig('context', 'cluster') == cluster_name }.dig('context', 'user')
new_clusters = clusters.reject { |c| c['name'] == cluster_name }
new_contexts = contexts.reject { |c| c.dig('context', 'cluster') == cluster_name }
new_users = users.reject { |c| c['name'] == target_user }

kubeconfig.merge({ 'clusters' => new_clusters, 'contexts' => new_contexts, 'users' => new_users })
end

def kubeconfig_env_path
Expand Down
2 changes: 1 addition & 1 deletion lib/uffizzi/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Uffizzi
VERSION = '2.0.11'
VERSION = '2.0.12'
end
Loading

0 comments on commit 57d76ec

Please sign in to comment.