Skip to content

Commit

Permalink
installation: Create new installation process
Browse files Browse the repository at this point in the history
Create new CNF installation process, that will support multiple deployments,
be more comprehensible and easier to maintain.
Don't replace the old one yet, both installation methods
should exist simultaneously

Refs: #2161
Signed-off-by: Konstantin Yarovoy <[email protected]>
  • Loading branch information
Konstantin Yarovoy committed Oct 21, 2024
1 parent 7ee8aa8 commit e432397
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 16 deletions.
21 changes: 21 additions & 0 deletions src/tasks/cnf_setup.cr
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,27 @@ task "sample_generic_cnf_cleanup" do |_, args|
CNFManager.sample_cleanup(config_file: "sample-cnfs/sample-generic-cnf", verbose: true)
end

task "new_cnf_setup" do |_, args|
if CNFManager.cnf_installed?
stdout_warning "A CNF is already set up. Setting up multiple CNFs is not allowed."
stdout_warning "To set up a new CNF, clean up the existing one by running: cnf_cleanup cnf-path=#{CNFManager.cnf_config_list.first}"
exit 0
end
if ClusterTools.install
stdout_success "ClusterTools installed"
else
stdout_failure "The ClusterTools installation timed out. Please check the status of the cluster-tools pods."
exit 1
end
stdout_success "CNF installation start."
CNFInstall.install_cnf(args)
stdout_success "CNF installation complete."
end

task "new_cnf_cleanup" do |_, args|
CNFInstall.uninstall_cnf()
end

def interactively_create_config
new_config = {
config_version: CNFInstall::Config::ConfigVersion::Latest.to_s,
Expand Down
3 changes: 3 additions & 0 deletions src/tasks/constants.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ require "./utils/embedded_file_manager.cr"

ESSENTIAL_PASSED_THRESHOLD = 15
CNF_DIR = "cnfs"
DEPLOYMENTS_DIR = File.join(CNF_DIR, "deployments")
CNF_TEMP_FILES_DIR = File.join(CNF_DIR, "temp_files")
CONFIG_FILE = "cnf-testsuite.yml"
BASE_CONFIG = "./config.yml"
COMMON_MANIFEST_FILE_PATH = "#{CNF_DIR}/common_manifest.yml"
DEPLOYMENT_MANIFEST_FILE_NAME = "deployment_manifest.yml"
PASSED = "passed"
FAILED = "failed"
SKIPPED = "skipped"
Expand Down
4 changes: 4 additions & 0 deletions src/tasks/utils/cnf_installation/config.cr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ module CNFInstall
end

def self.parse_cnf_config_from_file(path_to_config)
if !File.exists?(path_to_config)
stdout_failure "No config found at #{path_to_config}."
exit 1
end
yaml_content = File.read(path_to_config)
config_dir = CNFManager.ensure_cnf_testsuite_dir(path_to_config)
begin
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module CNFInstall
abstract class DeploymentManager
property deployment_name : String

abstract def install
abstract def uninstall
abstract def generate_manifest

def initialize(deployment_name)
@deployment_name = deployment_name
end
end


end
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
require "../config_versions/config_versions.cr"
require "./deployment_manager_common.cr"

module CNFInstall
abstract class HelmDeploymentManager < DeploymentManager
def initialize(deployment_name)
super(deployment_name)
end

abstract def get_deployment_name()
abstract def get_deployment_namespace()

def install_from_folder(chart_path, helm_namespace, helm_values)
begin
#TODO (kosstennbl) fix Helm install to add -n to namespace and remove it there
response = Helm.install(release_name: @deployment_name, helm_chart: chart_path, namespace: "-n #{helm_namespace}", values: helm_values)
if !response[:status].success?
stdout_failure "Helm installation failed, stderr:"
stdout_failure "\t#{response[:error]}"
exit 1
end
rescue e : Helm::InstallationFailed
stdout_failure "Helm installation failed with message:"
stdout_failure "\t#{e.message}"
exit 1
rescue e : Helm::CannotReuseReleaseNameError
stdout_failure "Helm deployment \"#{@deployment_name}\" already exists in \"#{helm_namespace}\" namespace."
stdout_failure "Change deployment name in CNF configuration or uninstall existing deployment."
exit 1
end
end

def uninstall()
deployment_name = get_deployment_name()
helm_uninstall_cmd = "#{deployment_name} -n #{get_deployment_namespace()}"
result = Helm.uninstall(helm_uninstall_cmd)
if result[:status].success?
stdout_success "Successfully uninstalled helm deployment \"#{deployment_name}\"."
end
end

def generate_manifest()
namespace = get_deployment_namespace()
generated_manifest = Helm.generate_manifest(get_deployment_name(), namespace)
generated_manifest_with_namespaces = Manifest.add_namespace_to_resources(generated_manifest, namespace)
end
end

class HelmChartDeploymentManager < HelmDeploymentManager
@helm_chart_config : ConfigV2::HelmChartConfig
def initialize(helm_chart_config)
super(helm_chart_config.name)
@helm_chart_config = helm_chart_config
end

def install()
helm_repo_url = @helm_chart_config.helm_repo_url
helm_repo_name = @helm_chart_config.helm_repo_name
helm_chart_name = @helm_chart_config.helm_chart_name

if !helm_repo_url.empty?
Helm.helm_repo_add(helm_repo_name, helm_repo_url)
end
helm_pull_destination = File.join(DEPLOYMENTS_DIR, @deployment_name)
helm_pull_cmd = "#{helm_repo_name}/#{helm_chart_name} --untar --destination #{helm_pull_destination}"
pull_response = Helm.pull(helm_pull_cmd)
if !pull_response[:status].success?
raise Helm::InstallationFailed.new("Helm pull failed: #{pull_response[:error]}")
end
chart_path = File.join(helm_pull_destination, helm_chart_name)
install_from_folder(chart_path, get_deployment_namespace(), @helm_chart_config.helm_values)
end

def get_deployment_name()
@helm_chart_config.name
end

def get_deployment_namespace()
@helm_chart_config.namespace.empty? ? DEFAULT_CNF_NAMESPACE : @helm_chart_config.namespace
end
end

class HelmDirectoryDeploymentManager < HelmDeploymentManager
@helm_directory_config : ConfigV2::HelmDirectoryConfig
def initialize(helm_directory_config)
super(helm_directory_config.name)
@helm_directory_config = helm_directory_config
end

def install()
chart_path = File.join(DEPLOYMENTS_DIR, @deployment_name, @helm_directory_config.helm_directory)
install_from_folder(chart_path, get_deployment_namespace(), @helm_directory_config.helm_values)
end

def get_deployment_name()
@helm_directory_config.name
end

def get_deployment_namespace()
@helm_directory_config.namespace.empty? ? DEFAULT_CNF_NAMESPACE : @helm_directory_config.namespace
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
require "../config_versions/config_versions.cr"
require "./deployment_manager_common.cr"


module CNFInstall
class ManifestDeploymentManager < DeploymentManager
@manifest_config : ConfigV2::ManifestDirectoryConfig
@manifest_directory_path : String

def initialize(manifest_config)
super(manifest_config.name)
@manifest_config = manifest_config
@manifest_directory_path = File.join(DEPLOYMENTS_DIR, @deployment_name, @manifest_config.manifest_directory)
end

def install()
KubectlClient::Apply.file(@manifest_directory_path)
end

def uninstall()
result = KubectlClient::Delete.file(@manifest_directory_path, wait: true)
if result[:status].success?
stdout_success "Successfully uninstalled manifest deployment \"#{@manifest_config.name}\""
end
end

def generate_manifest()
deployment_manifest = ""
list_of_manifests = Manifest.manifest_file_list(@manifest_directory_path)
list_of_manifests.each do |manifest_path|
manifest = File.read(manifest_path)
deployment_manifest = deployment_manifest + manifest + "\n"
end
deployment_manifest
end
end
end
155 changes: 139 additions & 16 deletions src/tasks/utils/cnf_installation/install_common.cr
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,147 @@ module CNFInstall
Invalid
end

def self.install_method_by_config_src(config_src : String) : InstallMethod
Log.info { "install_method_by_config_src" }
Log.info { "config_src: #{config_src}" }
helm_chart_file = "#{config_src}/#{Helm::CHART_YAML}"
Log.info { "looking for potential helm_chart_file: #{helm_chart_file}: file exists?: #{File.exists?(helm_chart_file)}" }

if !Dir.exists?(config_src)
Log.info { "install_method_by_config_src helm_chart selected" }
InstallMethod::HelmChart
elsif File.exists?(helm_chart_file)
Log.info { "install_method_by_config_src helm_directory selected" }
InstallMethod::HelmDirectory
elsif Dir.exists?(config_src)
Log.info { "install_method_by_config_src manifest_directory selected" }
InstallMethod::ManifestDirectory
def self.install_cnf(cli_args)
parsed_args = parse_cli_args(cli_args)
cnf_config_path = parsed_args[:config_path]
if cnf_config_path.empty?
stdout_failure "cnf-config or cnf-path parameter with valid CNF configuration should be provided."
exit(1)
end
config = Config.parse_cnf_config_from_file(cnf_config_path)
ensure_cnf_dir_structure()
FileUtils.cp(cnf_config_path, CNF_DIR)

prepare_deployment_directories(config, cnf_config_path)

deployment_managers = create_deployment_manager_list(config)
install_deployments(parsed_args: parsed_args, deployment_managers: deployment_managers)
end

def self.parse_cli_args(cli_args)
Log.for("cnf_setup").debug { "cli_args = #{cli_args.inspect}" }
cnf_config_path = ""
timeout = 1800
skip_wait_for_install = cli_args.raw.includes? "skip_wait_for_install"

if cli_args.named.keys.includes? "cnf-config"
cnf_config_path = cli_args.named["cnf-config"].as(String)
elsif cli_args.named.keys.includes? "cnf-path"
cnf_config_path = cli_args.named["cnf-path"].as(String)
end
cnf_config_path = self.ensure_cnf_config_path_file(cnf_config_path)

if cli_args.named.keys.includes? "timeout"
timeout = cli_args.named["timeout"].to_i
end
parsed_args = {config_path: cnf_config_path, timeout: timeout, skip_wait_for_install: skip_wait_for_install}
Log.for("cnf_setup").debug { "parsed_cli_args = #{parsed_args}"}
parsed_args
end

def self.ensure_cnf_config_path_file(path)
if CNFManager.path_has_yml?(path)
yml = path
else
puts "Error: #{config_src} is neither a helm_chart, helm_directory, or manifest_directory.".colorize(:red)
yml = File.join(path, CONFIG_FILE)
end
end

def self.ensure_cnf_dir_structure()
FileUtils.mkdir_p(CNF_DIR)
FileUtils.mkdir_p(DEPLOYMENTS_DIR)
FileUtils.mkdir_p(CNF_TEMP_FILES_DIR)
end

def self.prepare_deployment_directories(config, cnf_config_path)
# Deployment names are expected to be unique (ensured in config)
config.deployments.helm_charts.each do |helm_chart_config|
FileUtils.mkdir_p(File.join(DEPLOYMENTS_DIR, helm_chart_config.name))
end
config.deployments.helm_dirs.each do |helm_directory_config|
source_dir = File.join(Path[cnf_config_path].dirname, helm_directory_config.helm_directory)
destination_dir = File.join(DEPLOYMENTS_DIR, helm_directory_config.name)
FileUtils.mkdir_p(destination_dir)
FileUtils.cp_r(source_dir, destination_dir)
end
config.deployments.manifests.each do |manifest_config|
source_dir = File.join(Path[cnf_config_path].dirname, manifest_config.manifest_directory)
destination_dir = File.join(DEPLOYMENTS_DIR, manifest_config.name)
FileUtils.mkdir_p(destination_dir)
FileUtils.cp_r(source_dir, destination_dir)
end
end

def self.create_deployment_manager_list(config)
deployment_managers = [] of DeploymentManager
config.deployments.helm_charts.each do |helm_chart_config|
deployment_managers << HelmChartDeploymentManager.new(helm_chart_config)
end
config.deployments.helm_dirs.each do |helm_directory_config|
deployment_managers << HelmDirectoryDeploymentManager.new(helm_directory_config)
end
config.deployments.manifests.each do |manifest_config|
deployment_managers << ManifestDeploymentManager.new(manifest_config)
end
deployment_managers
end

def self.install_deployments(parsed_args, deployment_managers)
deployment_managers.each do |deployment_manager|
deployment_name = deployment_manager.deployment_name

stdout_success "Installing deployment #{deployment_name}"
deployment_manager.install()

generated_deployment_manifest = deployment_manager.generate_manifest()
deployment_manifest_path = File.join(DEPLOYMENTS_DIR, deployment_name, DEPLOYMENT_MANIFEST_FILE_NAME)
Manifest.add_manifest_to_file(deployment_name, generated_deployment_manifest, deployment_manifest_path)
Manifest.add_manifest_to_file(deployment_name, generated_deployment_manifest, COMMON_MANIFEST_FILE_PATH)

if !parsed_args[:skip_wait_for_install]
wait_for_deployment_resources(deployment_name, generated_deployment_manifest, parsed_args[:timeout])
end
end
end

def self.wait_for_deployment_resources(deployment_name, deployment_manifest, timeout)
resources_info = Helm.workload_resource_kind_names(Manifest.manifest_string_to_ymls(deployment_manifest))
workload_resources_info = resources_info.select { |resource_info|
["replicaset", "deployment", "statefulset", "pod", "daemonset"].includes?(resource_info[:kind].downcase)
}
total_resource_count = workload_resources_info.size()
current_resource_number = 1
workload_resources_info.each do | resource_info |
stdout_success "Waiting for resource for \"#{deployment_name}\" deployment (#{current_resource_number}/#{total_resource_count}): [#{resource_info[:kind]}] #{resource_info[:name]}", same_line: true
ready = KubectlClient::Get.resource_wait_for_install(resource_info[:kind], resource_info[:name], wait_count: timeout, namespace: resource_info[:namespace])
if !ready
stdout_failure "\"#{deployment_name}\" deployment setup has timed-out, [#{resource_info[:kind]}] #{resource_info[:name]} is not ready after #{timeout} seconds.", same_line: true
stdout_failure "Recommended course of actions would be to investigate the resource in cluster, then call cnf_cleanup and try to reinstall the CNF."
exit 1
end
current_resource_number += 1
end
stdout_success "All \"#{deployment_name}\" deployment resources are up.", same_line: true
end

def self.uninstall_cnf()
cnf_config_path = File.join(CNF_DIR, CONFIG_FILE)
if !File.exists?(cnf_config_path)
stdout_failure "No CNF config found in #{CNF_DIR} directory. Nothing to uninstall"
exit 1
end
config = Config.parse_cnf_config_from_file(cnf_config_path)

deployment_managers = create_deployment_manager_list(config)
uninstall_deployments(deployment_managers)

FileUtils.rm_rf(CNF_DIR)
end

def self.uninstall_deployments(deployment_managers)
deployment_managers.each do |deployment_manager|
deployment_manager.uninstall()
end
stdout_success "All CNF deployments were uninstalled, some time might be needed for all resources to be down."
end
end

0 comments on commit e432397

Please sign in to comment.