diff --git a/src/tasks/utils/cnf_manager.cr b/src/tasks/utils/cnf_manager.cr index 7e96dfd8f..b7e0c371b 100644 --- a/src/tasks/utils/cnf_manager.cr +++ b/src/tasks/utils/cnf_manager.cr @@ -10,6 +10,407 @@ require "./config.cr" module CNFManager + module Points + def self.points_yml + # TODO get points.yml from remote http + points = File.open("points.yml") do |f| + YAML.parse(f) + end + # LOGGING.debug "points: #{points.inspect}" + points.as_a + end + def self.create_points_yml + unless File.exists?("#{POINTSFILE}") + branch = ENV.has_key?("SCORING_ENV") ? ENV["SCORING_ENV"] : "master" + default_scoring_yml = "https://raw.githubusercontent.com/cncf/cnf-conformance/#{branch}/scoring_config/#{DEFAULT_POINTSFILENAME}" + `wget #{ENV.has_key?("SCORING_YML") ? ENV["SCORING_YML"] : default_scoring_yml}` + `mv #{DEFAULT_POINTSFILENAME} #{POINTSFILE}` + end + end + + def self.create_final_results_yml_name + FileUtils.mkdir_p("results") unless Dir.exists?("results") + "results/cnf-conformance-results-" + Time.local.to_s("%Y%m%d-%H%M%S-%L") + ".yml" + end + + def self.clean_results_yml(verbose=false) + if File.exists?("#{CNFManager::Points::Results.file}") + results = File.open("#{CNFManager::Points::Results.file}") do |f| + YAML.parse(f) + end + File.open("#{CNFManager::Points::Results.file}", "w") do |f| + YAML.dump({name: results["name"], + status: results["status"], + exit_code: results["exit_code"], + points: results["points"], + items: [] of YAML::Any}, f) + end + end + end + + def self.task_points(task, passed=true) + if passed + field_name = "pass" + else + field_name = "fail" + end + points =CNFManager::Points.points_yml.find {|x| x["name"] == task} + LOGGING.warn "****Warning**** task #{task} not found in points.yml".colorize(:yellow) unless points + if points && points[field_name]? + points[field_name].as_i if points + else + points =CNFManager::Points.points_yml.find {|x| x["name"] == "default_scoring"} + points[field_name].as_i if points + end + end + + def self.total_points(tag=nil) + if tag + tasks = CNFManager::Points.tasks_by_tag(tag) + else + tasks = CNFManager::Points.all_task_test_names + end + yaml = File.open("#{CNFManager::Points::Results.file}") do |file| + YAML.parse(file) + end + yaml["items"].as_a.reduce(0) do |acc, i| + if i["points"].as_i? && i["name"].as_s? && + tasks.find{|x| x == i["name"]} + (acc + i["points"].as_i) + else + acc + end + end + end + + def self.total_max_points(tag=nil) + if tag + tasks = CNFManager::Points.tasks_by_tag(tag) + else + tasks = CNFManager::Points.all_task_test_names + end + tasks.reduce(0) do |acc, x| + points = CNFManager::Points.task_points(x) + if points + acc + points + else + acc + end + end + end + + def self.upsert_task(task, status, points) + results = File.open("#{CNFManager::Points::Results.file}") do |f| + YAML.parse(f) + end + + result_items = results["items"].as_a + # remove the existing entry + result_items = result_items.reject do |x| + x["name"] == task + end + + result_items << YAML.parse "{name: #{task}, status: #{status}, points: #{points}}" + File.open("#{CNFManager::Points::Results.file}", "w") do |f| + YAML.dump({name: results["name"], + status: results["status"], + points: results["points"], + exit_code: results["exit_code"], + items: result_items}, f) + end + end + + def self.failed_task(task, msg) + CNFManager::Points.upsert_task(task, FAILED, CNFManager::Points.task_points(task, false)) + stdout_failure "#{msg}" + end + + def self.passed_task(task, msg) + CNFManager::Points.upsert_task(task, PASSED, CNFManager::Points.task_points(task)) + stdout_success "#{msg}" + end + + def self.failed_required_tasks + yaml = File.open("#{CNFManager::Points::Results.file}") do |file| + YAML.parse(file) + end + yaml["items"].as_a.reduce([] of String) do |acc, i| + if i["status"].as_s == "failed" && + i["name"].as_s? && + CNFManager::Points.task_required(i["name"].as_s) + (acc << i["name"].as_s) + else + acc + end + end + end + + def self.task_required(task) + points =CNFManager::Points.points_yml.find {|x| x["name"] == task} + LOGGING.warn "task #{task} not found in points.yml".colorize(:yellow) unless points + if points && points["required"]? && points["required"].as_bool == true + true + else + false + end + end + + def self.all_task_test_names + result_items =CNFManager::Points.points_yml.reduce([] of String) do |acc, x| + if x["name"].as_s == "default_scoring" || + x["tags"].as_s.split(",").find{|x|x=="platform"} + acc + else + acc << x["name"].as_s + end + end + end + + def self.tasks_by_tag(tag) + #TODO cross reference points.yml tags with results + found = false + result_items =CNFManager::Points.points_yml.reduce([] of String) do |acc, x| + if x["tags"].as_s? && x["tags"].as_s.includes?(tag) + acc << x["name"].as_s + else + acc + end + end + end + + def self.all_result_test_names(results_file) + results = File.open(results_file) do |f| + YAML.parse(f) + end + result_items = results["items"].as_a.reduce([] of String) do |acc, x| + acc << x["name"].as_s + end + end + + def self.results_by_tag(tag) + task_list = tasks_by_tag(tag) + + results = File.open("#{CNFManager::Points::Results.file}") do |f| + YAML.parse(f) + end + + found = false + result_items = results["items"].as_a.reduce([] of YAML::Any) do |acc, x| + if x["name"].as_s? && task_list.find{|tl| tl == x["name"].as_s} + acc << x + else + acc + end + end + end + + class Results + @@file : String + @@file = CNFManager::Points.create_final_results_yml_name + LOGGING.info "CNFManager::Points::Results.file" + continue = false + LOGGING.info "file exists?:#{File.exists?(@@file)}" + if File.exists?("#{@@file}") + stdout_info "Do you wish to overwrite the #{@@file} file? If so, your previous results.yml will be lost." + print "(Y/N) (Default N): > " + if ENV["CRYSTAL_ENV"]? == "TEST" + continue = true + else + user_input = gets + if user_input == "Y" || user_input == "y" + continue = true + end + end + else + continue = true + end + if continue + File.open("#{@@file}", "w") do |f| + YAML.dump(CNFManager::Points.template_results_yml, f) + end + end + def self.file + @@file + end + end + + def self.template_results_yml + #TODO add tags for category summaries + YAML.parse <<-END +name: cnf conformance +status: +points: +exit_code: 0 +items: [] +END + end + end + + module Task + def self.task_runner(args, &block : Sam::Args, CNFManager::Config -> String | Colorize::Object(String) | Nil) + LOGGING.info("task_runner args: #{args.inspect}") + if check_cnf_config(args) + CNFManager::Task.single_task_runner(args, &block) + else + CNFManager::Task.all_cnfs_task_runner(args, &block) + end + end + + # TODO give example for calling + def CNFManager::Task.all_cnfs_task_runner(args, &block : Sam::Args, CNFManager::Config -> String | Colorize::Object(String) | Nil) + + # Platforms tests dont have any cnfs + if CNFManager.cnf_config_list(silent: true).size == 0 + CNFManager::Task.single_task_runner(args, &block) + else + CNFManager.cnf_config_list(silent: true).map do |x| + new_args = Sam::Args.new(args.named, args.raw) + new_args.named["cnf-config"] = x + CNFManager::Task.single_task_runner(new_args, &block) + end + end + end + # TODO give example for calling + def CNFManager::Task.single_task_runner(args, &block : Sam::Args, CNFManager::Config -> String | Colorize::Object(String) | Nil) + LOGGING.debug("single_task_runner args: #{args.inspect}") + begin + if args.named["cnf-config"]? # platform tests don't have a cnf-config + config = CNFManager::Config.parse_config_yml(args.named["cnf-config"].as(String)) + else + config = CNFManager::Config.new({ destination_cnf_dir: "", + source_cnf_file: "", + source_cnf_dir: "", + yml_file_path: "", + install_method: {:helm_chart, ""}, + manifest_directory: "", + helm_directory: "", + helm_chart_path: "", + manifest_file_path: "", + git_clone_url: "", + install_script: "", + release_name: "", + service_name: "", + docker_repository: "", + helm_repository: {name: "", repo_url: ""}, + helm_chart: "", + helm_chart_container_name: "", + rolling_update_tag: "", + container_names: [{"name" => "", "rolling_update_test_tag" => ""}], + white_list_container_names: [""]} ) + end + yield args, config + rescue ex + # Set exception key/value in results + # file to -1 + update_yml("#{CNFManager::Points::Results.file}", "exit_code", "1") + LOGGING.error ex.message + ex.backtrace.each do |x| + LOGGING.error x + end + end + end + end + + class Config + def initialize(cnf_config) + @cnf_config = cnf_config + end + property cnf_config : NamedTuple(destination_cnf_dir: String, + source_cnf_file: String, + source_cnf_dir: String, + yml_file_path: String, + install_method: Tuple(Symbol, String), + manifest_directory: String, + helm_directory: String, + helm_chart_path: String, + manifest_file_path: String, + git_clone_url: String, + install_script: String, + release_name: String, + service_name: String, + docker_repository: String, + helm_repository: NamedTuple(name: String, + repo_url: String) | Nil, + helm_chart: String, + helm_chart_container_name: String, + rolling_update_tag: String, + container_names: Array(Hash(String, String )) | Nil, + white_list_container_names: Array(String)) + + def self.parse_config_yml(config_yml_path : String) : CNFManager::Config + LOGGING.debug "parse_config_yml config_yml_path: #{config_yml_path}" + yml_file = CNFManager.ensure_cnf_conformance_yml_path(config_yml_path) + config = CNFManager.parsed_config_file(yml_file) + + install_method = CNFManager.cnf_installation_method(config) + + CNFManager.generate_and_set_release_name(config_yml_path) + + destination_cnf_dir = CNFManager.cnf_destination_dir(yml_file) + + yml_file_path = CNFManager.ensure_cnf_conformance_dir(config_yml_path) + source_cnf_file = yml_file + source_cnf_dir = yml_file_path + manifest_directory = optional_key_as_string(config, "manifest_directory") + if config["helm_repository"]? + helm_repository = config["helm_repository"].as_h + helm_repo_name = optional_key_as_string(helm_repository, "name") + helm_repo_url = optional_key_as_string(helm_repository, "repo_url") + else + helm_repo_name = "" + helm_repo_url = "" + end + helm_chart = optional_key_as_string(config, "helm_chart") + release_name = optional_key_as_string(config, "release_name") + service_name = optional_key_as_string(config, "service_name") + helm_directory = optional_key_as_string(config, "helm_directory") + git_clone_url = optional_key_as_string(config, "git_clone_url") + install_script = optional_key_as_string(config, "install_script") + docker_repository = optional_key_as_string(config, "docker_repository") + if helm_directory.empty? + working_chart_directory = "exported_chart" + else + working_chart_directory = helm_directory + end + helm_chart_path = destination_cnf_dir + "/" + working_chart_directory + manifest_file_path = destination_cnf_dir + "/" + "temp_template.yml" + white_list_container_names = config.get("white_list_helm_chart_container_names").as_a.map do |c| + "#{c.as_s?}" + end + container_names_totem = config["container_names"] + container_names = container_names_totem.as_a.map do |container| + {"name" => optional_key_as_string(container, "name"), + "rolling_update_test_tag" => optional_key_as_string(container, "rolling_update_test_tag"), + "rolling_downgrade_test_tag" => optional_key_as_string(container, "rolling_downgrade_test_tag"), + "rolling_version_change_test_tag" => optional_key_as_string(container, "rolling_version_change_test_tag"), + "rollback_from_tag" => optional_key_as_string(container, "rollback_from_tag"), + } + end + + CNFManager::Config.new({ destination_cnf_dir: destination_cnf_dir, + source_cnf_file: source_cnf_file, + source_cnf_dir: source_cnf_dir, + yml_file_path: yml_file_path, + install_method: install_method, + manifest_directory: manifest_directory, + helm_directory: helm_directory, + helm_chart_path: helm_chart_path, + manifest_file_path: manifest_file_path, + git_clone_url: git_clone_url, + install_script: install_script, + release_name: release_name, + service_name: service_name, + docker_repository: docker_repository, + helm_repository: {name: helm_repo_name, repo_url: helm_repo_url}, + helm_chart: helm_chart, + helm_chart_container_name: "", + rolling_update_tag: "", + container_names: container_names, + white_list_container_names: white_list_container_names }) + + end + end + # Applies a block to each cnf resource # # `CNFManager.cnf_workload_resources(args, config) {|cnf_config, resource| #your code} @@ -234,6 +635,7 @@ module CNFManager def self.generate_and_set_release_name(config_yml_path) LOGGING.info "generate_and_set_release_name" yml_file = CNFManager.ensure_cnf_conformance_yml_path(config_yml_path) + yml_path = CNFManager.ensure_cnf_conformance_dir(config_yml_path) config = CNFManager.parsed_config_file(yml_file) predefined_release_name = optional_key_as_string(config, "release_name") @@ -246,8 +648,8 @@ module CNFManager LOGGING.debug "helm_chart install method: #{install_method[1]}" release_name = helm_chart_template_release_name(install_method[1]) when :helm_directory - LOGGING.debug "helm_directory install method: #{yml_file}/#{install_method[1]}" - release_name = helm_chart_template_release_name("#{yml_file}/#{install_method[1]}") + LOGGING.debug "helm_directory install method: #{yml_path}/#{install_method[1]}" + release_name = helm_chart_template_release_name("#{yml_path}/#{install_method[1]}") when :manifest_directory LOGGING.debug "manifest_directory install method" release_name = UUID.random.to_s