From 7abb628a1eb7201935f6d6ede05c61fb4bcaf5f6 Mon Sep 17 00:00:00 2001 From: Zipofar Date: Tue, 8 Aug 2023 20:00:41 +0300 Subject: [PATCH 1/6] [1110_uffizzi_platform] WIP --- lib/uffizzi/cli.rb | 4 + lib/uffizzi/cli/install.rb | 283 ++++++++++++++++++++++++ lib/uffizzi/services/preview_service.rb | 4 +- test/support/mocks/mock_shell.rb | 18 +- test/uffizzi/cli/install_test.rb | 53 +++++ test/uffizzi/cli/login_test.rb | 2 - 6 files changed, 360 insertions(+), 4 deletions(-) create mode 100644 lib/uffizzi/cli/install.rb create mode 100644 test/uffizzi/cli/install_test.rb diff --git a/lib/uffizzi/cli.rb b/lib/uffizzi/cli.rb index e6a99137..146e7ade 100644 --- a/lib/uffizzi/cli.rb +++ b/lib/uffizzi/cli.rb @@ -75,6 +75,10 @@ def disconnect(credential_type) require_relative 'cli/dev' subcommand 'dev', Cli::Dev + desc 'install', 'install' + require_relative 'cli/install' + subcommand 'install', Cli::Install + map preview: :compose class << self diff --git a/lib/uffizzi/cli/install.rb b/lib/uffizzi/cli/install.rb new file mode 100644 index 00000000..76845f87 --- /dev/null +++ b/lib/uffizzi/cli/install.rb @@ -0,0 +1,283 @@ +# frozen_string_literal: true + +require 'uffizzi' +require 'uffizzi/config_file' + +module Uffizzi + class Cli::Install < Thor + HELM_REPO_NAME = 'uffizzi' + HELM_DEPLOYED_STATUS = 'deployed' + CHART_NAME = 'uffizzi-app' + VALUES_FILE_NAME = 'helm_values.yaml' + + desc 'by-wizard [NAMESPACE]', 'Install uffizzi to cluster' + def by_wizard(namespace) + run_installation do + ask_installation_params(namespace) + end + end + + desc 'by-options [NAMESPACE]', 'Install uffizzi to cluster' + method_option :domain, required: true, type: :string, aliases: '-d' + method_option :'user-email', required: false, type: :string, aliases: '-e' + method_option :'acme-email', required: false, type: :string + method_option :'user-password', required: false, type: :string + method_option :'controller-password', required: false, type: :string + method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'], default: 'letsencrypt' + method_option :'wildcard-cert-path', required: false, type: :string + method_option :'wildcard-key-path', required: false, type: :string + method_option :'without-wildcard-tls', required: false, type: :boolean + def by_options(namespace) + run_installation do + validate_installation_options(namespace, options) + end + end + + desc 'add-wildcard-tls [NAMESPACE]', 'Add wildcard tls from files' + method_option :cert, required: true, type: :string, aliases: '-c' + method_option :key, required: true, type: :string, aliases: '-k' + method_option :domain, required: true, type: :string, aliases: '-d' + def add_wildcard_tls(namespace) + kubectl_exists? + + params = { + namespace: namespace, + domain: options[:domain], + wildcard_cert_path: options[:cert], + wildcard_key_path: options[:key], + } + + kubectl_add_wildcard_tls(params) + end + + private + + def run_installation + kubectl_exists? + helm_exists? + params = yield + helm_values = build_helm_values(params) + create_helm_values_file(helm_values) + helm_set_repo + helm_set_release(params.fetch(:namespace)) + kubectl_add_wildcard_tls(params) if params[:wildcard_cert_path] && params[:wildcard_key_path] + end + + def kubectl_exists? + cmd = 'kubectl version -o json' + execute_command(cmd, say: false).present? + end + + def helm_exists? + cmd = 'helm version --short' + execute_command(cmd, say: false).present? + end + + def helm_set_repo + repo = helm_repo_search + return if repo.present? + + helm_repo_add + end + + def helm_set_release(namespace) + releases = helm_release_list(namespace) + release = releases.detect { |r| r['name'] == namespace } + if release.present? + Uffizzi.ui.say_error_and_exit("The release #{release['name']} already exists with status #{release['status']}") + end + + helm_install(namespace) + end + + def helm_repo_add + cmd = "helm repo add #{HELM_REPO_NAME} https://uffizzicloud.github.io/uffizzi" + execute_command(cmd) + end + + def helm_repo_search + cmd = "helm search repo #{HELM_REPO_NAME}/#{CHART_NAME} -o json" + + execute_command(cmd) do |result, err| + err.present? ? nil : JSON.parse(result) + end + end + + def helm_release_list(namespace) + cmd = "helm list -n #{namespace} -o json" + result = execute_command(cmd, say: false) + + JSON.parse(result) + end + + def helm_install(namespace) + release_name = namespace + cmd = "helm install #{release_name} #{HELM_REPO_NAME}/#{CHART_NAME}" \ + " --values #{helm_values_file_path}" \ + " --namespace #{namespace}" \ + ' --create-namespace' \ + ' --output json' + + res = execute_command(cmd, say: false) + info = JSON.parse(res)['info'] + + return Uffizzi.ui.say('Helm release is deployed') if info['status'] == HELM_DEPLOYED_STATUS + + Uffizzi.ui.say_error_and_exit(info) + end + + def kubectl_add_wildcard_tls(params) + cmd = "kubectl create secret tls wildcard.#{params.fetch(:domain)}" \ + " --cert=#{params.fetch(:wildcard_cert_path)}" \ + " --key=#{params.fetch(:wildcard_key_path)}" \ + " --namespace #{params.fetch(:namespace)}" + + execute_command(cmd) + end + + def ask_wildcard_cert + has_user_wildcard_cert = Uffizzi.prompt.yes?('Uffizzi use a wildcard tls certificate. Do you have it?') + + if has_user_wildcard_cert + cert_path = Uffizzi.prompt.ask('Path to cert: ', required: true) + key_path = Uffizzi.prompt.ask('Path to key: ', required: true) + + return { wildcard_cert_path: cert_path, wildcard_key_path: key_path } + end + + add_later = Uffizzi.prompt.yes?('Do you want to add wildcard certificate later?') + + if add_later + Uffizzi.ui.say('You can set command "uffizzi install add-wildcard-cert [NAMESPACE]'\ + ' -d your.domain.com -c /path/to/cert -k /path/to/key"') + + { wildcard_cert_path: nil, wildcard_key_path: nil } + else + Uffizzi.ui.say('Sorry, but uffizzi can not work correctly without wildcard certificate') + exit(0) + end + end + + def ask_installation_params(namespace) + wildcard_cert_paths = ask_wildcard_cert + domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') + user_email = Uffizzi.prompt.ask('User email: ', required: true, default: "admin@#{domain}") + user_password = Uffizzi.prompt.ask('User password: ', required: true, default: generate_password) + controller_password = Uffizzi.prompt.ask('Controller password: ', required: true, default: generate_password) + cert_email = Uffizzi.prompt.ask('Email address for ACME registration: ', required: true, default: user_email) + cluster_issuers = [ + { name: 'Letsencrypt', value: 'letsencrypt' }, + { name: 'ZeroSSL', value: 'zerossl' }, + ] + cluster_issuer = Uffizzi.prompt.select('Cluster issuer', cluster_issuers) + + { + namespace: namespace, + domain: domain, + user_email: user_email, + user_password: user_password, + controller_password: controller_password, + cert_email: cert_email, + cluster_issuer: cluster_issuer, + }.merge(wildcard_cert_paths) + end + + def validate_installation_options(namespace, options) + base_params = { + namespace: namespace, + domain: options[:domain], + user_email: options[:'user-email'] || "admin@#{options[:domain]}", + user_password: options[:'user-password'] || generate_password, + controller_password: options[:'controller-password'] || generate_password, + cert_email: options[:'acme-email'] || options[:'user-email'], + cluster_issuer: options[:issuer], + wildcard_cert_path: nil, + wildcard_key_path: nil, + } + + return base_params if options[:'without-wildcard-tls'] + + empty_key = [:'wildcard-cert-path', :'wildcard-key-path'].detect { |k| options[k].nil? } + + if empty_key.present? + return Uffizzi.ui.say_error_and_exit("#{empty_key} is required or use the flag without-wildcard-tls") + end + + wildcard_params = { + wildcard_cert_path: options[:'wildcard-cert-path'], + wildcard_key_path: options[:'wildcard-key-path'], + } + + base_params.merge(wildcard_params) + end + + def build_helm_values(params) + domain = params.fetch(:domain) + namespace = params.fetch(:namespace) + app_host = ['app', domain].join('.') + + { + app_url: "https://#{app_host}", + webHostname: app_host, + allowed_hosts: app_host, + managed_dns_zone_dns_name: domain, + global: { + uffizzi: { + firstUser: { + email: params.fetch(:user_email), + password: params.fetch(:user_password), + }, + controller: { + password: params.fetch(:controller_password), + }, + }, + }, + 'uffizzi-controller' => { + ingress: { + hostname: "controller.#{domain}", + }, + clusterIssuer: params.fetch(:cluster_issuer), + certEmail: params.fetch(:cert_email), + 'ingress-nginx' => { + controller: { + extraArgs: { + 'default-ssl-certificate' => "#{namespace}/wildcard.#{domain}", + }, + }, + }, + }, + }.deep_stringify_keys + end + + def execute_command(command, say: true) + stdout_str, stderr_str, status = Uffizzi.ui.execute(command) + + return yield(stdout_str, stderr_str) if block_given? + + Uffizzi.ui.say_error_and_exit(stderr_str) unless status.success? + + say ? Uffizzi.ui.say(stdout_str) : stdout_str + rescue Errno::ENOENT => e + Uffizzi.ui.say_error_and_exit(e.message) + end + + def create_helm_values_file(values) + FileUtils.mkdir_p(helm_values_dir_path) unless File.directory?(helm_values_dir_path) + File.write(helm_values_file_path, values.to_yaml) + end + + def helm_values_file_path + File.join(helm_values_dir_path, VALUES_FILE_NAME) + end + + def helm_values_dir_path + File.dirname(Uffizzi::ConfigFile.config_path) + end + + def generate_password + hexatridecimal_base = 36 + length = 8 + rand(hexatridecimal_base**length).to_s(hexatridecimal_base) + end + end +end diff --git a/lib/uffizzi/services/preview_service.rb b/lib/uffizzi/services/preview_service.rb index 0f7c4e13..98b58db4 100644 --- a/lib/uffizzi/services/preview_service.rb +++ b/lib/uffizzi/services/preview_service.rb @@ -56,7 +56,9 @@ def wait_containers_creation(deployment, project_slug) Uffizzi.ui.say('Deployed') Uffizzi.ui.say("Deployment url: https://#{deployment[:preview_url]}") - Uffizzi.ui.say("Deployment proxy url: https://#{deployment[:proxy_preview_url]}") + if deployment[:proxy_preview_url].present? + Uffizzi.ui.say("Deployment proxy url: https://#{deployment[:proxy_preview_url]}") + end activity_items rescue ApiClient::ResponseError => e diff --git a/test/support/mocks/mock_shell.rb b/test/support/mocks/mock_shell.rb index ce1cf024..dc03dec7 100644 --- a/test/support/mocks/mock_shell.rb +++ b/test/support/mocks/mock_shell.rb @@ -2,7 +2,6 @@ class MockShell class ExitError < StandardError; end - class MockProcessStatus def initialize(success) @success = success @@ -109,6 +108,23 @@ def promise_execute(command, stdout: nil, stderr: nil, waiter: nil) private + def get_command_response(command) + response_index = @command_responses.index do |command_response| + case command_response[:command] + when Regexp + command_response[:command].match?(command) + else + command_response[:command] == command + end + end + + stdout = @command_responses[response_index].fetch(:stdout) + stderr = @command_responses[response_index].fetch(:stderr) + @command_responses.delete_at(response_index) + + [stdout, stderr] + end + def format_to_json(data) data.to_json end diff --git a/test/uffizzi/cli/install_test.rb b/test/uffizzi/cli/install_test.rb new file mode 100644 index 00000000..46170f9c --- /dev/null +++ b/test/uffizzi/cli/install_test.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +require 'psych' +require 'base64' +require 'test_helper' + +class InstallTest < Minitest::Test + def setup + @install = Uffizzi::Cli::Install.new + + tmp_dir_name = (Time.now.utc.to_f * 100_000).to_i + helm_values_path = "/tmp/test/#{tmp_dir_name}/helm_values.yaml" + Uffizzi::ConfigFile.stubs(:config_path).returns(helm_values_path) + end + + def test_install_by_wizard + @mock_prompt.promise_question_answer('Uffizzi use a wildcard tls certificate. Do you have it?', 'n') + @mock_prompt.promise_question_answer('Do you want to add wildcard certificate later?', 'y') + @mock_prompt.promise_question_answer('Domain: ', 'my-domain.com') + @mock_prompt.promise_question_answer('User email: ', 'admin@my-domain.com') + @mock_prompt.promise_question_answer('User password: ', 'password') + @mock_prompt.promise_question_answer('Controller password: ', 'password') + @mock_prompt.promise_question_answer('Email address for ACME registration: ', 'admin@my-domain.com') + @mock_prompt.promise_question_answer('Cluster issuer', :first) + + @mock_shell.promise_execute(/kubectl version/, stdout: '1.23.00') + @mock_shell.promise_execute(/helm version/, stdout: '3.00') + @mock_shell.promise_execute(/helm search repo/, stdout: [].to_json) + @mock_shell.promise_execute(/helm repo add/, stdout: 'ok') + @mock_shell.promise_execute(/helm list/, stdout: [].to_json) + @mock_shell.promise_execute(/helm install/, stdout: { info: { status: 'deployed' } }.to_json) + + @install.by_wizard('uffizzi') + + last_message = Uffizzi.ui.last_message + assert_match('deployed', last_message) + end + + def test_install_by_options + @mock_shell.promise_execute(/kubectl version/, stdout: '1.23.00') + @mock_shell.promise_execute(/helm version/, stdout: '3.00') + @mock_shell.promise_execute(/helm search repo/, stdout: [].to_json) + @mock_shell.promise_execute(/helm repo add/, stdout: 'ok') + @mock_shell.promise_execute(/helm list/, stdout: [].to_json) + @mock_shell.promise_execute(/helm install/, stdout: { info: { status: 'deployed' } }.to_json) + + @install.options = command_options(domain: 'my-domain.com', 'without-wildcard-tls' => true) + @install.by_options('uffizzi') + + last_message = Uffizzi.ui.last_message + assert_match('deployed', last_message) + end +end diff --git a/test/uffizzi/cli/login_test.rb b/test/uffizzi/cli/login_test.rb index a03ba12b..732da2a1 100644 --- a/test/uffizzi/cli/login_test.rb +++ b/test/uffizzi/cli/login_test.rb @@ -5,8 +5,6 @@ class LoginTest < Minitest::Test def setup @cli = Uffizzi::Cli.new - @mock_prompt = MockPrompt.new - Uffizzi.stubs(:prompt).returns(@mock_prompt) @command_params = { username: generate(:email), From 24fe9b31330afcd0ad25038dd78858b027029a84 Mon Sep 17 00:00:00 2001 From: Zipofar Date: Thu, 31 Aug 2023 19:29:00 +0300 Subject: [PATCH 2/6] [1110_uffizzi_platform] refactor --- lib/uffizzi/cli/install.rb | 70 ++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/lib/uffizzi/cli/install.rb b/lib/uffizzi/cli/install.rb index 76845f87..7e5852ce 100644 --- a/lib/uffizzi/cli/install.rb +++ b/lib/uffizzi/cli/install.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require 'byebug' require 'uffizzi' require 'uffizzi/config_file' @@ -9,39 +10,40 @@ class Cli::Install < Thor HELM_DEPLOYED_STATUS = 'deployed' CHART_NAME = 'uffizzi-app' VALUES_FILE_NAME = 'helm_values.yaml' + DEFAULT_ISSUER = 'letsencrypt' + DEFAULT_NAMESPACE = 'default' - desc 'by-wizard [NAMESPACE]', 'Install uffizzi to cluster' - def by_wizard(namespace) - run_installation do - ask_installation_params(namespace) - end - end - - desc 'by-options [NAMESPACE]', 'Install uffizzi to cluster' - method_option :domain, required: true, type: :string, aliases: '-d' - method_option :'user-email', required: false, type: :string, aliases: '-e' + desc 'application', 'Install uffizzi to cluster' + method_option :namespace, required: false, type: :string + method_option :domain, required: false, type: :string + method_option :'user-email', required: false, type: :string method_option :'acme-email', required: false, type: :string method_option :'user-password', required: false, type: :string method_option :'controller-password', required: false, type: :string - method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'], default: 'letsencrypt' + method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'] method_option :'wildcard-cert-path', required: false, type: :string method_option :'wildcard-key-path', required: false, type: :string method_option :'without-wildcard-tls', required: false, type: :boolean - def by_options(namespace) + def application run_installation do - validate_installation_options(namespace, options) + if options.present? + validate_installation_options + else + ask_installation_params + end end end - desc 'add-wildcard-tls [NAMESPACE]', 'Add wildcard tls from files' - method_option :cert, required: true, type: :string, aliases: '-c' - method_option :key, required: true, type: :string, aliases: '-k' - method_option :domain, required: true, type: :string, aliases: '-d' - def add_wildcard_tls(namespace) + desc 'wildcard-tls', 'Add wildcard tls from files' + method_option :domain, required: true, type: :string + method_option :cert, required: true, type: :string + method_option :key, required: true, type: :string + method_option :namespace, required: false, type: :string + def add_wildcard_tls kubectl_exists? params = { - namespace: namespace, + namespace: options[:namespace], domain: options[:domain], wildcard_cert_path: options[:cert], wildcard_key_path: options[:key], @@ -111,6 +113,8 @@ def helm_release_list(namespace) end def helm_install(namespace) + Uffizzi.ui.say('Start helm release installation') + release_name = namespace cmd = "helm install #{release_name} #{HELM_REPO_NAME}/#{CHART_NAME}" \ " --values #{helm_values_file_path}" \ @@ -145,25 +149,19 @@ def ask_wildcard_cert return { wildcard_cert_path: cert_path, wildcard_key_path: key_path } end - add_later = Uffizzi.prompt.yes?('Do you want to add wildcard certificate later?') - - if add_later - Uffizzi.ui.say('You can set command "uffizzi install add-wildcard-cert [NAMESPACE]'\ - ' -d your.domain.com -c /path/to/cert -k /path/to/key"') + Uffizzi.ui.say('Uffizzi does not work properly without a wildcard certificate.') + Uffizzi.ui.say('You can add wildcard cert later with command:') + Uffizzi.ui.say('uffizzi install wildcard-tls --domain your.domain.com --cert /path/to/cert --key /path/to/key') - { wildcard_cert_path: nil, wildcard_key_path: nil } - else - Uffizzi.ui.say('Sorry, but uffizzi can not work correctly without wildcard certificate') - exit(0) - end + { wildcard_cert_path: nil, wildcard_key_path: nil } end - def ask_installation_params(namespace) + def ask_installation_params wildcard_cert_paths = ask_wildcard_cert + namespace = Uffizzi.prompt.ask('Namespace: ', required: true, default: DEFAULT_NAMESPACE) domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') user_email = Uffizzi.prompt.ask('User email: ', required: true, default: "admin@#{domain}") user_password = Uffizzi.prompt.ask('User password: ', required: true, default: generate_password) - controller_password = Uffizzi.prompt.ask('Controller password: ', required: true, default: generate_password) cert_email = Uffizzi.prompt.ask('Email address for ACME registration: ', required: true, default: user_email) cluster_issuers = [ { name: 'Letsencrypt', value: 'letsencrypt' }, @@ -176,21 +174,21 @@ def ask_installation_params(namespace) domain: domain, user_email: user_email, user_password: user_password, - controller_password: controller_password, + controller_password: generate_password, cert_email: cert_email, cluster_issuer: cluster_issuer, }.merge(wildcard_cert_paths) end - def validate_installation_options(namespace, options) + def validate_installation_options base_params = { - namespace: namespace, + namespace: options[:namespace] || DEFAULT_NAMESPACE, domain: options[:domain], user_email: options[:'user-email'] || "admin@#{options[:domain]}", user_password: options[:'user-password'] || generate_password, controller_password: options[:'controller-password'] || generate_password, cert_email: options[:'acme-email'] || options[:'user-email'], - cluster_issuer: options[:issuer], + cluster_issuer: options[:issuer] || DEFAULT_ISSUER, wildcard_cert_path: nil, wildcard_key_path: nil, } @@ -200,7 +198,7 @@ def validate_installation_options(namespace, options) empty_key = [:'wildcard-cert-path', :'wildcard-key-path'].detect { |k| options[k].nil? } if empty_key.present? - return Uffizzi.ui.say_error_and_exit("#{empty_key} is required or use the flag without-wildcard-tls") + return Uffizzi.ui.say_error_and_exit("#{empty_key} is required or use the flag --without-wildcard-tls") end wildcard_params = { From 00952c53cc11dc568bc9a707094f34311bb1a36c Mon Sep 17 00:00:00 2001 From: Zipofar Date: Fri, 1 Sep 2023 19:21:10 +0300 Subject: [PATCH 3/6] [1110_uffizzi_platform] refactor --- lib/uffizzi/cli/install.rb | 117 +++++++++++++++++++------------ test/uffizzi/cli/install_test.rb | 5 +- 2 files changed, 76 insertions(+), 46 deletions(-) diff --git a/lib/uffizzi/cli/install.rb b/lib/uffizzi/cli/install.rb index 7e5852ce..2bd03d6f 100644 --- a/lib/uffizzi/cli/install.rb +++ b/lib/uffizzi/cli/install.rb @@ -12,21 +12,24 @@ class Cli::Install < Thor VALUES_FILE_NAME = 'helm_values.yaml' DEFAULT_ISSUER = 'letsencrypt' DEFAULT_NAMESPACE = 'default' + DEFAULT_APP_PREFIX = 'uffizzi' desc 'application', 'Install uffizzi to cluster' - method_option :namespace, required: false, type: :string - method_option :domain, required: false, type: :string - method_option :'user-email', required: false, type: :string - method_option :'acme-email', required: false, type: :string - method_option :'user-password', required: false, type: :string - method_option :'controller-password', required: false, type: :string + method_option :namespace, type: :string + method_option :domain, type: :string + method_option :'user-email', type: :string + method_option :'acme-email', type: :string + method_option :'user-password', type: :string + method_option :'controller-password', type: :string method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'] - method_option :'wildcard-cert-path', required: false, type: :string - method_option :'wildcard-key-path', required: false, type: :string - method_option :'without-wildcard-tls', required: false, type: :boolean + method_option :'wildcard-cert-path', type: :string + method_option :'wildcard-key-path', type: :string + method_option :'without-wildcard-tls', type: :boolean + method_option :repo, type: :string + method_option :'print-values', type: :boolean def application run_installation do - if options.present? + if options.except(:repo, :'print-values').present? validate_installation_options else ask_installation_params @@ -35,34 +38,56 @@ def application end desc 'wildcard-tls', 'Add wildcard tls from files' - method_option :domain, required: true, type: :string - method_option :cert, required: true, type: :string - method_option :key, required: true, type: :string - method_option :namespace, required: false, type: :string - def add_wildcard_tls + method_option :domain, type: :string + method_option :cert, type: :string + method_option :key, type: :string + method_option :namespace, type: :string + def wildcard_tls kubectl_exists? - params = { - namespace: options[:namespace], - domain: options[:domain], - wildcard_cert_path: options[:cert], - wildcard_key_path: options[:key], - } + params = if options.present? && wildcard_tls_options_valid? + { + namespace: options[:namespace] || DEFAULT_NAMESPACE, + domain: options[:domain], + wildcard_cert_path: options[:cert], + wildcard_key_path: options[:key], + } + else + namespace = Uffizzi.prompt.ask('Namespace: ', required: true, default: DEFAULT_NAMESPACE) + domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') + wildcard_cert_paths = ask_wildcard_cert(has_user_wildcard_cert: true) + + { namespace: namespace, domain: domain }.merge(wildcard_cert_paths) + end kubectl_add_wildcard_tls(params) end private + def wildcard_tls_options_valid? + required_options = [:domain, :cert, :key] + missing_options = required_options - options.symbolize_keys.keys + + return true if missing_options.empty? + + rendered_missing_options = missing_options.map { |o| "'--#{o}'" }.join(', ') + + Uffizzi.ui.say_error_and_exit("No value provided for required options #{rendered_missing_options}") + end + def run_installation kubectl_exists? helm_exists? params = yield helm_values = build_helm_values(params) + return Uffizzi.ui.say(helm_values.to_yaml) if options[:'print-values'] + create_helm_values_file(helm_values) - helm_set_repo + helm_set_repo unless options[:repo] helm_set_release(params.fetch(:namespace)) kubectl_add_wildcard_tls(params) if params[:wildcard_cert_path] && params[:wildcard_key_path] + delete_helm_values_file end def kubectl_exists? @@ -116,7 +141,8 @@ def helm_install(namespace) Uffizzi.ui.say('Start helm release installation') release_name = namespace - cmd = "helm install #{release_name} #{HELM_REPO_NAME}/#{CHART_NAME}" \ + repo = options[:repo] || "#{HELM_REPO_NAME}/#{CHART_NAME}" + cmd = "helm install #{release_name} #{repo}" \ " --values #{helm_values_file_path}" \ " --namespace #{namespace}" \ ' --create-namespace' \ @@ -139,8 +165,8 @@ def kubectl_add_wildcard_tls(params) execute_command(cmd) end - def ask_wildcard_cert - has_user_wildcard_cert = Uffizzi.prompt.yes?('Uffizzi use a wildcard tls certificate. Do you have it?') + def ask_wildcard_cert(has_user_wildcard_cert: nil) + has_user_wildcard_cert ||= Uffizzi.prompt.yes?('Uffizzi use a wildcard tls certificate. Do you have it?') if has_user_wildcard_cert cert_path = Uffizzi.prompt.ask('Path to cert: ', required: true) @@ -153,7 +179,7 @@ def ask_wildcard_cert Uffizzi.ui.say('You can add wildcard cert later with command:') Uffizzi.ui.say('uffizzi install wildcard-tls --domain your.domain.com --cert /path/to/cert --key /path/to/key') - { wildcard_cert_path: nil, wildcard_key_path: nil } + {} end def ask_installation_params @@ -181,38 +207,37 @@ def ask_installation_params end def validate_installation_options - base_params = { - namespace: options[:namespace] || DEFAULT_NAMESPACE, - domain: options[:domain], - user_email: options[:'user-email'] || "admin@#{options[:domain]}", - user_password: options[:'user-password'] || generate_password, - controller_password: options[:'controller-password'] || generate_password, - cert_email: options[:'acme-email'] || options[:'user-email'], - cluster_issuer: options[:issuer] || DEFAULT_ISSUER, - wildcard_cert_path: nil, - wildcard_key_path: nil, - } + installation_options = build_installation_options - return base_params if options[:'without-wildcard-tls'] + if options[:'without-wildcard-tls'] + return installation_options.except(:wildcard_cert_path, :wildcard_key_path) + end empty_key = [:'wildcard-cert-path', :'wildcard-key-path'].detect { |k| options[k].nil? } if empty_key.present? - return Uffizzi.ui.say_error_and_exit("#{empty_key} is required or use the flag --without-wildcard-tls") + Uffizzi.ui.say_error_and_exit("#{empty_key} is required or use the flag --without-wildcard-tls") end + end - wildcard_params = { + def build_installation_options + { + namespace: options[:namespace] || DEFAULT_NAMESPACE, + domain: options[:domain], + user_email: options[:'user-email'] || "admin@#{options[:domain]}", + user_password: options[:'user-password'] || generate_password, + controller_password: options[:'controller-password'] || generate_password, + cert_email: options[:'acme-email'] || options[:'user-email'], + cluster_issuer: options[:issuer] || DEFAULT_ISSUER, wildcard_cert_path: options[:'wildcard-cert-path'], wildcard_key_path: options[:'wildcard-key-path'], } - - base_params.merge(wildcard_params) end def build_helm_values(params) domain = params.fetch(:domain) namespace = params.fetch(:namespace) - app_host = ['app', domain].join('.') + app_host = [DEFAULT_APP_PREFIX, domain].join('.') { app_url: "https://#{app_host}", @@ -232,7 +257,7 @@ def build_helm_values(params) }, 'uffizzi-controller' => { ingress: { - hostname: "controller.#{domain}", + disabled: true, }, clusterIssuer: params.fetch(:cluster_issuer), certEmail: params.fetch(:cert_email), @@ -264,6 +289,10 @@ def create_helm_values_file(values) File.write(helm_values_file_path, values.to_yaml) end + def delete_helm_values_file + File.delete(helm_values_file_path) if File.exist?(helm_values_file_path) + end + def helm_values_file_path File.join(helm_values_dir_path, VALUES_FILE_NAME) end diff --git a/test/uffizzi/cli/install_test.rb b/test/uffizzi/cli/install_test.rb index 46170f9c..42911fc1 100644 --- a/test/uffizzi/cli/install_test.rb +++ b/test/uffizzi/cli/install_test.rb @@ -16,6 +16,7 @@ def setup def test_install_by_wizard @mock_prompt.promise_question_answer('Uffizzi use a wildcard tls certificate. Do you have it?', 'n') @mock_prompt.promise_question_answer('Do you want to add wildcard certificate later?', 'y') + @mock_prompt.promise_question_answer('Namespace: ', 'uffizzi') @mock_prompt.promise_question_answer('Domain: ', 'my-domain.com') @mock_prompt.promise_question_answer('User email: ', 'admin@my-domain.com') @mock_prompt.promise_question_answer('User password: ', 'password') @@ -30,7 +31,7 @@ def test_install_by_wizard @mock_shell.promise_execute(/helm list/, stdout: [].to_json) @mock_shell.promise_execute(/helm install/, stdout: { info: { status: 'deployed' } }.to_json) - @install.by_wizard('uffizzi') + @install.application last_message = Uffizzi.ui.last_message assert_match('deployed', last_message) @@ -45,7 +46,7 @@ def test_install_by_options @mock_shell.promise_execute(/helm install/, stdout: { info: { status: 'deployed' } }.to_json) @install.options = command_options(domain: 'my-domain.com', 'without-wildcard-tls' => true) - @install.by_options('uffizzi') + @install.application last_message = Uffizzi.ui.last_message assert_match('deployed', last_message) From ef00a516ce42395e881dbf41bfdb85990810e713 Mon Sep 17 00:00:00 2001 From: Zipofar Date: Mon, 4 Sep 2023 20:03:39 +0300 Subject: [PATCH 4/6] [1110_uffizzi_platform] add man --- docker-compose.yml | 2 + lib/uffizzi/cli/install.rb | 66 +++++++++++++-------------- man/uffizzi-install | 76 ++++++++++++++++++++++++++++++++ man/uffizzi-install.ronn | 67 ++++++++++++++++++++++++++++ test/uffizzi/cli/install_test.rb | 9 ++-- 5 files changed, 182 insertions(+), 38 deletions(-) create mode 100644 man/uffizzi-install create mode 100644 man/uffizzi-install.ronn diff --git a/docker-compose.yml b/docker-compose.yml index 17799ffd..ebb40c33 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ services: - ~/.ssh:/root/.ssh - ~/.bash_history:/root/.bash_history - ~/.config/uffizzi:/root/.config/uffizzi + - ~/test/uffizzi_app/charts/uffizzi-app:/gem/tmp/charts/uffizzi_app + - ~/test/uffizzi_controller_os/charts/uffizzi-controller:/gem/tmp/charts/uffizzi-controller - bundle_cache:/bundle_cache environment: - BUNDLE_PATH=/bundle_cache diff --git a/lib/uffizzi/cli/install.rb b/lib/uffizzi/cli/install.rb index 2bd03d6f..9f1bf77f 100644 --- a/lib/uffizzi/cli/install.rb +++ b/lib/uffizzi/cli/install.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'byebug' require 'uffizzi' require 'uffizzi/config_file' @@ -11,7 +10,7 @@ class Cli::Install < Thor CHART_NAME = 'uffizzi-app' VALUES_FILE_NAME = 'helm_values.yaml' DEFAULT_ISSUER = 'letsencrypt' - DEFAULT_NAMESPACE = 'default' + DEFAULT_NAMESPACE = 'uffizzi' DEFAULT_APP_PREFIX = 'uffizzi' desc 'application', 'Install uffizzi to cluster' @@ -20,7 +19,6 @@ class Cli::Install < Thor method_option :'user-email', type: :string method_option :'acme-email', type: :string method_option :'user-password', type: :string - method_option :'controller-password', type: :string method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'] method_option :'wildcard-cert-path', type: :string method_option :'wildcard-key-path', type: :string @@ -42,10 +40,11 @@ def application method_option :cert, type: :string method_option :key, type: :string method_option :namespace, type: :string + method_option :repo, type: :string def wildcard_tls kubectl_exists? - params = if options.present? && wildcard_tls_options_valid? + params = if options.except(:repo).present? && wildcard_tls_options_valid? { namespace: options[:namespace] || DEFAULT_NAMESPACE, domain: options[:domain], @@ -55,18 +54,25 @@ def wildcard_tls else namespace = Uffizzi.prompt.ask('Namespace: ', required: true, default: DEFAULT_NAMESPACE) domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') - wildcard_cert_paths = ask_wildcard_cert(has_user_wildcard_cert: true) + wildcard_cert_paths = ask_wildcard_cert(has_user_wildcard_cert: true, domain: domain) { namespace: namespace, domain: domain }.merge(wildcard_cert_paths) end kubectl_add_wildcard_tls(params) + helm_values = helm_get_values(namespace, namespace) + helm_values['uffizzi-controller']['tlsPerDeploymentEnabled'] = false.to_s + create_helm_values_file(helm_values) + helm_set_repo unless options[:repo] + helm_install(release_name: namespace, namespace: namespace, repo: options[:repo]) end + default_task :application + private def wildcard_tls_options_valid? - required_options = [:domain, :cert, :key] + required_options = [:namespace, :domain, :cert, :key] missing_options = required_options - options.symbolize_keys.keys return true if missing_options.empty? @@ -85,9 +91,12 @@ def run_installation create_helm_values_file(helm_values) helm_set_repo unless options[:repo] - helm_set_release(params.fetch(:namespace)) + helm_install(release_name: params[:namespace], namespace: params[:namespace], repo: options[:repo]) kubectl_add_wildcard_tls(params) if params[:wildcard_cert_path] && params[:wildcard_key_path] delete_helm_values_file + + Uffizzi.ui.say('Helm release is deployed') + Uffizzi.ui.say("The uffizzi application url is https://#{DEFAULT_APP_PREFIX}.#{params[:domain]}") end def kubectl_exists? @@ -107,16 +116,6 @@ def helm_set_repo helm_repo_add end - def helm_set_release(namespace) - releases = helm_release_list(namespace) - release = releases.detect { |r| r['name'] == namespace } - if release.present? - Uffizzi.ui.say_error_and_exit("The release #{release['name']} already exists with status #{release['status']}") - end - - helm_install(namespace) - end - def helm_repo_add cmd = "helm repo add #{HELM_REPO_NAME} https://uffizzicloud.github.io/uffizzi" execute_command(cmd) @@ -130,32 +129,31 @@ def helm_repo_search end end - def helm_release_list(namespace) - cmd = "helm list -n #{namespace} -o json" - result = execute_command(cmd, say: false) - - JSON.parse(result) - end - - def helm_install(namespace) + def helm_install(release_name:, namespace:, repo:) Uffizzi.ui.say('Start helm release installation') - release_name = namespace - repo = options[:repo] || "#{HELM_REPO_NAME}/#{CHART_NAME}" - cmd = "helm install #{release_name} #{repo}" \ + repo = repo || "#{HELM_REPO_NAME}/#{CHART_NAME}" + cmd = "helm upgrade #{release_name} #{repo}" \ " --values #{helm_values_file_path}" \ " --namespace #{namespace}" \ ' --create-namespace' \ + ' --install' \ ' --output json' res = execute_command(cmd, say: false) info = JSON.parse(res)['info'] - return Uffizzi.ui.say('Helm release is deployed') if info['status'] == HELM_DEPLOYED_STATUS + return if info['status'] == HELM_DEPLOYED_STATUS Uffizzi.ui.say_error_and_exit(info) end + def helm_get_values(release_name, namespace) + cmd = "helm get values #{release_name} -n #{namespace} -o json" + res = execute_command(cmd, say: false) + JSON.parse(res) + end + def kubectl_add_wildcard_tls(params) cmd = "kubectl create secret tls wildcard.#{params.fetch(:domain)}" \ " --cert=#{params.fetch(:wildcard_cert_path)}" \ @@ -165,7 +163,7 @@ def kubectl_add_wildcard_tls(params) execute_command(cmd) end - def ask_wildcard_cert(has_user_wildcard_cert: nil) + def ask_wildcard_cert(has_user_wildcard_cert: nil, domain: nil) has_user_wildcard_cert ||= Uffizzi.prompt.yes?('Uffizzi use a wildcard tls certificate. Do you have it?') if has_user_wildcard_cert @@ -177,13 +175,12 @@ def ask_wildcard_cert(has_user_wildcard_cert: nil) Uffizzi.ui.say('Uffizzi does not work properly without a wildcard certificate.') Uffizzi.ui.say('You can add wildcard cert later with command:') - Uffizzi.ui.say('uffizzi install wildcard-tls --domain your.domain.com --cert /path/to/cert --key /path/to/key') + Uffizzi.ui.say("uffizzi install wildcard-tls --domain #{domain} --cert /path/to/cert --key /path/to/key") {} end def ask_installation_params - wildcard_cert_paths = ask_wildcard_cert namespace = Uffizzi.prompt.ask('Namespace: ', required: true, default: DEFAULT_NAMESPACE) domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') user_email = Uffizzi.prompt.ask('User email: ', required: true, default: "admin@#{domain}") @@ -194,6 +191,7 @@ def ask_installation_params { name: 'ZeroSSL', value: 'zerossl' }, ] cluster_issuer = Uffizzi.prompt.select('Cluster issuer', cluster_issuers) + wildcard_cert_paths = ask_wildcard_cert(domain: domain) { namespace: namespace, @@ -226,7 +224,7 @@ def build_installation_options domain: options[:domain], user_email: options[:'user-email'] || "admin@#{options[:domain]}", user_password: options[:'user-password'] || generate_password, - controller_password: options[:'controller-password'] || generate_password, + controller_password: generate_password, cert_email: options[:'acme-email'] || options[:'user-email'], cluster_issuer: options[:issuer] || DEFAULT_ISSUER, wildcard_cert_path: options[:'wildcard-cert-path'], @@ -237,6 +235,7 @@ def build_installation_options def build_helm_values(params) domain = params.fetch(:domain) namespace = params.fetch(:namespace) + tls_per_deployment_enabled = params.slice(:wildcard_cert_pathm, :wildcard_key_path).compact.empty? app_host = [DEFAULT_APP_PREFIX, domain].join('.') { @@ -260,6 +259,7 @@ def build_helm_values(params) disabled: true, }, clusterIssuer: params.fetch(:cluster_issuer), + tlsPerDeploymentEnabled: tls_per_deployment_enabled.to_s, certEmail: params.fetch(:cert_email), 'ingress-nginx' => { controller: { diff --git a/man/uffizzi-install b/man/uffizzi-install new file mode 100644 index 00000000..a13338b5 --- /dev/null +++ b/man/uffizzi-install @@ -0,0 +1,76 @@ +.\" generated with Ronn-NG/v0.9.1 +.\" http://github.com/apjanke/ronn-ng/tree/0.9.1 +.TH "INSTALL" "" "September 2023" "" +.SH "NAME" +\fBinstall\fR \- install the Uffizzi application to cluster +.SH "SYNOPSIS" +.nf +uffizzi install COMMAND +.fi +.SH "DESCRIPTION" +.nf +The uffizzi install command lets you deploy uffizzi application to your kubecrnetes cluster\. +If COMMAND is not specified, uffizzi install start installation\. +if OPTIONS not specified, uffizzi show installation wizard\. + +For more information on configuration options, see: +https://docs\.uffizzi\.com/references/cli/ +.fi +.SH "COMMANDS" +.nf +COMMAND is one of the following: + + wildcard_tls OPTION + Add the wildcard tls certificate to installed uffizzi application\. +.fi +.SH "OPTIONS" +.nf + OPTION is one of the following: + + namespace + The namespace of the kubecrnetes cluster where application will be deployed\. + Default is uffizzi\. + + domain + The domain that will be used for access the web API\. + + issuer + The cluster issuer that will be used for generate tls certificates\. + Default is letsencrypt\. + + user\-email + The login that will be used for access to web API\. + + user\-password + The password that will be used for access to web API\. + + acme\-email + Email address for ACME registration + + wildcard\-cert\-path + Path to wildcard certificate\. + + wildcard\-key\-path + Path to wildcard certificate key\. + + without\-wildcard\-tls + Set this flag and we can install application without wildcard certificate\. + + print\-values + Show builded vales for helm installation\. + The installation will not be executed\. + + repo + The repository that will be used for helm install +.fi +.SH "EXAMPLES" +.nf +To install the uffizzi command, run: + + $ uffizzi install + +To install the wildcard_tls command, run: + + $ uffizzi install wildcard_tls +.fi + diff --git a/man/uffizzi-install.ronn b/man/uffizzi-install.ronn new file mode 100644 index 00000000..c9f6cf62 --- /dev/null +++ b/man/uffizzi-install.ronn @@ -0,0 +1,67 @@ +uffizzi install - install the Uffizzi application to cluster +================================================================ + +## SYNOPSIS + uffizzi install COMMAND + +## DESCRIPTION + The uffizzi install command lets you deploy uffizzi application to your kubecrnetes cluster. + If COMMAND is not specified, uffizzi install start installation. + if OPTIONS not specified, uffizzi show installation wizard. + + For more information on configuration options, see: + https://docs.uffizzi.com/references/cli/ + +## COMMANDS + COMMAND is one of the following: + + wildcard_tls OPTION + Add the wildcard tls certificate to installed uffizzi application. + +## OPTIONS + OPTION is one of the following: + + namespace + The namespace of the kubecrnetes cluster where application will be deployed. + Default is uffizzi. + + domain + The domain that will be used for access the web API. + + issuer + The cluster issuer that will be used for generate tls certificates. + Default is letsencrypt. + + user-email + The login that will be used for access to web API. + + user-password + The password that will be used for access to web API. + + acme-email + Email address for ACME registration + + wildcard-cert-path + Path to wildcard certificate. + + wildcard-key-path + Path to wildcard certificate key. + + without-wildcard-tls + Set this flag and we can install application without wildcard certificate. + + print-values + Show builded vales for helm installation. + The installation will not be executed. + + repo + The repository that will be used for helm install + +## EXAMPLES + To install the uffizzi command, run: + + $ uffizzi install + + To install the wildcard_tls command, run: + + $ uffizzi install wildcard_tls diff --git a/test/uffizzi/cli/install_test.rb b/test/uffizzi/cli/install_test.rb index 42911fc1..a7cea326 100644 --- a/test/uffizzi/cli/install_test.rb +++ b/test/uffizzi/cli/install_test.rb @@ -29,12 +29,12 @@ def test_install_by_wizard @mock_shell.promise_execute(/helm search repo/, stdout: [].to_json) @mock_shell.promise_execute(/helm repo add/, stdout: 'ok') @mock_shell.promise_execute(/helm list/, stdout: [].to_json) - @mock_shell.promise_execute(/helm install/, stdout: { info: { status: 'deployed' } }.to_json) + @mock_shell.promise_execute(/helm upgrade/, stdout: { info: { status: 'deployed' } }.to_json) @install.application last_message = Uffizzi.ui.last_message - assert_match('deployed', last_message) + assert_match('The uffizzi application url is', last_message) end def test_install_by_options @@ -42,13 +42,12 @@ def test_install_by_options @mock_shell.promise_execute(/helm version/, stdout: '3.00') @mock_shell.promise_execute(/helm search repo/, stdout: [].to_json) @mock_shell.promise_execute(/helm repo add/, stdout: 'ok') - @mock_shell.promise_execute(/helm list/, stdout: [].to_json) - @mock_shell.promise_execute(/helm install/, stdout: { info: { status: 'deployed' } }.to_json) + @mock_shell.promise_execute(/helm upgrade/, stdout: { info: { status: 'deployed' } }.to_json) @install.options = command_options(domain: 'my-domain.com', 'without-wildcard-tls' => true) @install.application last_message = Uffizzi.ui.last_message - assert_match('deployed', last_message) + assert_match('The uffizzi application url is', last_message) end end From 6ce22327802ab249aa2b5d11a17db4da016cd865 Mon Sep 17 00:00:00 2001 From: Zipofar Date: Thu, 7 Sep 2023 18:45:54 +0300 Subject: [PATCH 5/6] [1110_uffizzi_platform] update installation --- lib/uffizzi/cli/install.rb | 77 +++++++++++++++++++++----------- test/uffizzi/cli/install_test.rb | 18 ++++---- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/lib/uffizzi/cli/install.rb b/lib/uffizzi/cli/install.rb index 9f1bf77f..bc0dd993 100644 --- a/lib/uffizzi/cli/install.rb +++ b/lib/uffizzi/cli/install.rb @@ -9,15 +9,14 @@ class Cli::Install < Thor HELM_DEPLOYED_STATUS = 'deployed' CHART_NAME = 'uffizzi-app' VALUES_FILE_NAME = 'helm_values.yaml' - DEFAULT_ISSUER = 'letsencrypt' DEFAULT_NAMESPACE = 'uffizzi' DEFAULT_APP_PREFIX = 'uffizzi' + DEFAULT_CLUSTER_ISSUER = 'letsencrypt' desc 'application', 'Install uffizzi to cluster' method_option :namespace, type: :string method_option :domain, type: :string method_option :'user-email', type: :string - method_option :'acme-email', type: :string method_option :'user-password', type: :string method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'] method_option :'wildcard-cert-path', type: :string @@ -53,7 +52,7 @@ def wildcard_tls } else namespace = Uffizzi.prompt.ask('Namespace: ', required: true, default: DEFAULT_NAMESPACE) - domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') + domain = Uffizzi.prompt.ask('Root Domain: ', required: true, default: 'example.com') wildcard_cert_paths = ask_wildcard_cert(has_user_wildcard_cert: true, domain: domain) { namespace: namespace, domain: domain }.merge(wildcard_cert_paths) @@ -89,14 +88,36 @@ def run_installation helm_values = build_helm_values(params) return Uffizzi.ui.say(helm_values.to_yaml) if options[:'print-values'] + namespace = params[:namespace] + release_name = params[:namespace] + create_helm_values_file(helm_values) helm_set_repo unless options[:repo] - helm_install(release_name: params[:namespace], namespace: params[:namespace], repo: options[:repo]) + helm_install(release_name: release_name, namespace: namespace, repo: options[:repo]) kubectl_add_wildcard_tls(params) if params[:wildcard_cert_path] && params[:wildcard_key_path] delete_helm_values_file + ingress_ip = get_web_ingress_ip_address(release_name, namespace) + Uffizzi.ui.say('Helm release is deployed') - Uffizzi.ui.say("The uffizzi application url is https://#{DEFAULT_APP_PREFIX}.#{params[:domain]}") + Uffizzi.ui.say("The uffizzi application url is 'https://#{DEFAULT_APP_PREFIX}.#{params[:domain]}'") + Uffizzi.ui.say("Create a DNS A record for domain '*.#{params[:domain]}' with value '#{ingress_ip}'") + end + + def get_web_ingress_ip_address(release_name, namespace) + Uffizzi.ui.say('Getting an ingress ip address...') + + 10.times do + web_ingress = kubectl_get_web_ingress(release_name, namespace) + ingresses = web_ingress.dig('status', 'loadBalancer', 'ingress') || [] + ip_address = ingresses.first&.fetch('ip', nil) + + return ip_address if ip_address.present? + + sleep(1) + end + + Uffizzi.ui.say_error_and_exit('We can`t get the uffizzi ingress ip address') end def kubectl_exists? @@ -163,34 +184,38 @@ def kubectl_add_wildcard_tls(params) execute_command(cmd) end + def kubectl_get_web_ingress(release_name, namespace) + cmd = "kubectl get ingress/#{release_name}-web-ingress -n #{namespace} -o json" + + res = execute_command(cmd, say: false) + JSON.parse(res) + end + def ask_wildcard_cert(has_user_wildcard_cert: nil, domain: nil) has_user_wildcard_cert ||= Uffizzi.prompt.yes?('Uffizzi use a wildcard tls certificate. Do you have it?') - if has_user_wildcard_cert - cert_path = Uffizzi.prompt.ask('Path to cert: ', required: true) - key_path = Uffizzi.prompt.ask('Path to key: ', required: true) + if !has_user_wildcard_cert + Uffizzi.ui.say('Uffizzi does not work properly without a wildcard certificate.') + Uffizzi.ui.say('You can add wildcard cert later with command:') + Uffizzi.ui.say("uffizzi install wildcard-tls --domain #{domain} --cert /path/to/cert --key /path/to/key") - return { wildcard_cert_path: cert_path, wildcard_key_path: key_path } + return {} end - Uffizzi.ui.say('Uffizzi does not work properly without a wildcard certificate.') - Uffizzi.ui.say('You can add wildcard cert later with command:') - Uffizzi.ui.say("uffizzi install wildcard-tls --domain #{domain} --cert /path/to/cert --key /path/to/key") + cert_path = Uffizzi.prompt.ask('Path to cert: ', required: true) + Uffizzi.ui.say_error_and_exit("File '#{cert_path}' does not exists") unless File.exist?(cert_path) + + key_path = Uffizzi.prompt.ask('Path to key: ', required: true) + Uffizzi.ui.say_error_and_exit("File '#{key_path}' does not exists") unless File.exist?(key_path) - {} + { wildcard_cert_path: cert_path, wildcard_key_path: key_path } end def ask_installation_params namespace = Uffizzi.prompt.ask('Namespace: ', required: true, default: DEFAULT_NAMESPACE) - domain = Uffizzi.prompt.ask('Domain: ', required: true, default: 'example.com') - user_email = Uffizzi.prompt.ask('User email: ', required: true, default: "admin@#{domain}") - user_password = Uffizzi.prompt.ask('User password: ', required: true, default: generate_password) - cert_email = Uffizzi.prompt.ask('Email address for ACME registration: ', required: true, default: user_email) - cluster_issuers = [ - { name: 'Letsencrypt', value: 'letsencrypt' }, - { name: 'ZeroSSL', value: 'zerossl' }, - ] - cluster_issuer = Uffizzi.prompt.select('Cluster issuer', cluster_issuers) + domain = Uffizzi.prompt.ask('Root domain: ', required: true, default: 'example.com') + user_email = Uffizzi.prompt.ask('First user email: ', required: true, default: "admin@#{domain}") + user_password = Uffizzi.prompt.ask('First user password: ', required: true, default: generate_password) wildcard_cert_paths = ask_wildcard_cert(domain: domain) { @@ -199,8 +224,8 @@ def ask_installation_params user_email: user_email, user_password: user_password, controller_password: generate_password, - cert_email: cert_email, - cluster_issuer: cluster_issuer, + cert_email: user_email, + cluster_issuer: DEFAULT_CLUSTER_ISSUER, }.merge(wildcard_cert_paths) end @@ -225,8 +250,8 @@ def build_installation_options user_email: options[:'user-email'] || "admin@#{options[:domain]}", user_password: options[:'user-password'] || generate_password, controller_password: generate_password, - cert_email: options[:'acme-email'] || options[:'user-email'], - cluster_issuer: options[:issuer] || DEFAULT_ISSUER, + cert_email: options[:'user-email'], + cluster_issuer: options[:issuer] || DEFAULT_CLUSTER_ISSUER, wildcard_cert_path: options[:'wildcard-cert-path'], wildcard_key_path: options[:'wildcard-key-path'], } diff --git a/test/uffizzi/cli/install_test.rb b/test/uffizzi/cli/install_test.rb index a7cea326..5971ca6f 100644 --- a/test/uffizzi/cli/install_test.rb +++ b/test/uffizzi/cli/install_test.rb @@ -14,15 +14,11 @@ def setup end def test_install_by_wizard - @mock_prompt.promise_question_answer('Uffizzi use a wildcard tls certificate. Do you have it?', 'n') - @mock_prompt.promise_question_answer('Do you want to add wildcard certificate later?', 'y') @mock_prompt.promise_question_answer('Namespace: ', 'uffizzi') - @mock_prompt.promise_question_answer('Domain: ', 'my-domain.com') - @mock_prompt.promise_question_answer('User email: ', 'admin@my-domain.com') - @mock_prompt.promise_question_answer('User password: ', 'password') - @mock_prompt.promise_question_answer('Controller password: ', 'password') - @mock_prompt.promise_question_answer('Email address for ACME registration: ', 'admin@my-domain.com') - @mock_prompt.promise_question_answer('Cluster issuer', :first) + @mock_prompt.promise_question_answer('Root domain: ', 'my-domain.com') + @mock_prompt.promise_question_answer('First user email: ', 'admin@my-domain.com') + @mock_prompt.promise_question_answer('First user password: ', 'password') + @mock_prompt.promise_question_answer('Uffizzi use a wildcard tls certificate. Do you have it?', 'n') @mock_shell.promise_execute(/kubectl version/, stdout: '1.23.00') @mock_shell.promise_execute(/helm version/, stdout: '3.00') @@ -30,11 +26,12 @@ def test_install_by_wizard @mock_shell.promise_execute(/helm repo add/, stdout: 'ok') @mock_shell.promise_execute(/helm list/, stdout: [].to_json) @mock_shell.promise_execute(/helm upgrade/, stdout: { info: { status: 'deployed' } }.to_json) + @mock_shell.promise_execute(/kubectl get ingress/, stdout: { status: { loadBalancer: { ingress: [{ ip: '34.31.68.232' }] } } }.to_json) @install.application last_message = Uffizzi.ui.last_message - assert_match('The uffizzi application url is', last_message) + assert_match('Create a DNS A record for domain', last_message) end def test_install_by_options @@ -43,11 +40,12 @@ def test_install_by_options @mock_shell.promise_execute(/helm search repo/, stdout: [].to_json) @mock_shell.promise_execute(/helm repo add/, stdout: 'ok') @mock_shell.promise_execute(/helm upgrade/, stdout: { info: { status: 'deployed' } }.to_json) + @mock_shell.promise_execute(/kubectl get ingress/, stdout: { status: { loadBalancer: { ingress: [{ ip: '34.31.68.232' }] } } }.to_json) @install.options = command_options(domain: 'my-domain.com', 'without-wildcard-tls' => true) @install.application last_message = Uffizzi.ui.last_message - assert_match('The uffizzi application url is', last_message) + assert_match('Create a DNS A record for domain', last_message) end end From 2cfc14e8d613fbb4ba18c5b63b86ebd19b4b0108 Mon Sep 17 00:00:00 2001 From: Zipofar Date: Wed, 20 Sep 2023 12:31:58 +0300 Subject: [PATCH 6/6] [1110_uffizzi_platform] remove wildcard tls from installation --- lib/uffizzi/cli/install.rb | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/lib/uffizzi/cli/install.rb b/lib/uffizzi/cli/install.rb index bc0dd993..4a4c464f 100644 --- a/lib/uffizzi/cli/install.rb +++ b/lib/uffizzi/cli/install.rb @@ -19,15 +19,12 @@ class Cli::Install < Thor method_option :'user-email', type: :string method_option :'user-password', type: :string method_option :issuer, type: :string, enum: ['letsencrypt', 'zerossl'] - method_option :'wildcard-cert-path', type: :string - method_option :'wildcard-key-path', type: :string - method_option :'without-wildcard-tls', type: :boolean method_option :repo, type: :string method_option :'print-values', type: :boolean def application run_installation do if options.except(:repo, :'print-values').present? - validate_installation_options + build_installation_options else ask_installation_params end @@ -94,7 +91,6 @@ def run_installation create_helm_values_file(helm_values) helm_set_repo unless options[:repo] helm_install(release_name: release_name, namespace: namespace, repo: options[:repo]) - kubectl_add_wildcard_tls(params) if params[:wildcard_cert_path] && params[:wildcard_key_path] delete_helm_values_file ingress_ip = get_web_ingress_ip_address(release_name, namespace) @@ -229,20 +225,6 @@ def ask_installation_params }.merge(wildcard_cert_paths) end - def validate_installation_options - installation_options = build_installation_options - - if options[:'without-wildcard-tls'] - return installation_options.except(:wildcard_cert_path, :wildcard_key_path) - end - - empty_key = [:'wildcard-cert-path', :'wildcard-key-path'].detect { |k| options[k].nil? } - - if empty_key.present? - Uffizzi.ui.say_error_and_exit("#{empty_key} is required or use the flag --without-wildcard-tls") - end - end - def build_installation_options { namespace: options[:namespace] || DEFAULT_NAMESPACE, @@ -252,15 +234,12 @@ def build_installation_options controller_password: generate_password, cert_email: options[:'user-email'], cluster_issuer: options[:issuer] || DEFAULT_CLUSTER_ISSUER, - wildcard_cert_path: options[:'wildcard-cert-path'], - wildcard_key_path: options[:'wildcard-key-path'], } end def build_helm_values(params) domain = params.fetch(:domain) namespace = params.fetch(:namespace) - tls_per_deployment_enabled = params.slice(:wildcard_cert_pathm, :wildcard_key_path).compact.empty? app_host = [DEFAULT_APP_PREFIX, domain].join('.') { @@ -284,10 +263,13 @@ def build_helm_values(params) disabled: true, }, clusterIssuer: params.fetch(:cluster_issuer), - tlsPerDeploymentEnabled: tls_per_deployment_enabled.to_s, + tlsPerDeploymentEnabled: true.to_s, certEmail: params.fetch(:cert_email), 'ingress-nginx' => { controller: { + ingressClassResource: { + default: true, + }, extraArgs: { 'default-ssl-certificate' => "#{namespace}/wildcard.#{domain}", }, @@ -298,7 +280,7 @@ def build_helm_values(params) end def execute_command(command, say: true) - stdout_str, stderr_str, status = Uffizzi.ui.execute(command) + stdout_str, stderr_str, status = Uffizzi.ui.capture3(command) return yield(stdout_str, stderr_str) if block_given?