lib/uffizzi/services/compose_file_service.rb in uffizzi-cli-1.0.3 vs lib/uffizzi/services/compose_file_service.rb in uffizzi-cli-1.0.4

- old
+ new

@@ -1,42 +1,98 @@ # frozen_string_literal: true require 'psych' require 'pathname' require 'base64' +require 'minitar' +require 'zlib' +require 'uffizzi/services/project_service' +require 'uffizzi/services/volume_parser_service' class ComposeFileService + MAX_HOST_VOLUME_GZIP_FILE_SIZE = 1024 * 900 + DEPENDENCY_CONFIG_USE_KIND = :config_map + DEPENDENCY_VOLUME_USE_KIND = :volume + class << self - def parse(compose_content, compose_file_path) + def parse(compose_content, compose_file_dir) compose_data = parse_compose_content_to_object(compose_content) - env_files = prepare_services_env_files(compose_data['services']).flatten.uniq - config_files = fetch_configs(compose_data['configs']) - prepare_dependencies(env_files, config_files, compose_file_path) + services = compose_data['services'] + env_files_paths = prepare_env_files_paths(services).flatten.uniq + config_files_paths = prepare_config_files_paths(compose_data['configs']) + host_volumes_paths = prepare_host_volumes_paths(services) + prepare_dependencies(compose_file_dir, env_files_paths, config_files_paths, host_volumes_paths) end private - def prepare_dependencies(env_files, config_files, compose_file_path) - prepare_dependency_files_data(env_files + config_files, compose_file_path) + def prepare_dependencies(compose_file_dir, env_files_paths, config_files_paths, host_volumes_paths) + config_files_attrs = prepare_dependency_configs_files(env_files_paths + config_files_paths, compose_file_dir) + host_volumes_attrs = prepare_dependency_host_volumes_files(host_volumes_paths, compose_file_dir) + + config_files_attrs + host_volumes_attrs end - def prepare_dependency_files_data(dependency_files, compose_file_path) - dependency_files.map do |dependency_file| - dependency_file_data = Psych.load(File.read("#{compose_file_path}/#{dependency_file}")) + def prepare_dependency_configs_files(dependency_file_paths, compose_file_dir) + dependency_file_paths.map do |dependency_file_path| + dependency_file_content = Psych.load(File.read("#{compose_file_dir}/#{dependency_file_path}")) + { - path: dependency_file, - source: dependency_file, - content: Base64.encode64(dependency_file_data), + path: dependency_file_path, + source: dependency_file_path, + content: Base64.encode64(dependency_file_content), + use_kind: DEPENDENCY_CONFIG_USE_KIND, } end rescue Errno::ENOENT => e dependency_path = e.message.split('- ').last raise Uffizzi::Error.new("The config file #{dependency_path} does not exist") end - def fetch_configs(configs_data) + def prepare_dependency_host_volumes_files(dependency_file_paths, compose_file_dir) + base_dependency_paths = dependency_file_paths.map do |dependency_file_path| + dependency_pathname = Pathname.new(dependency_file_path) + next dependency_file_path if dependency_pathname.absolute? + next "#{compose_file_dir}/#{dependency_pathname.cleanpath}" if dependency_file_path.start_with?('./') + next "#{compose_file_dir}/#{dependency_pathname}" if dependency_file_path.start_with?('../') + + raise Uffizzi::Error.new("Unsupported path #{dependency_pathname}") + end + + base_dependency_paths.zip(dependency_file_paths).map do |base_dependency_path, dependency_file_path| + absolute_dependency_path = Pathname.new(base_dependency_path).realpath.to_s + dependency_file_content = prepare_host_volume_file_content(absolute_dependency_path) + + { + path: absolute_dependency_path, + source: dependency_file_path, + content: dependency_file_content, + use_kind: DEPENDENCY_VOLUME_USE_KIND, + is_file: Pathname.new(absolute_dependency_path).file?, + } + end + rescue Errno::ENOENT => e + dependency_path = e.message.split('- ').last + raise Uffizzi::Error.new("No such file or directory: #{dependency_path}") + end + + def prepare_host_volume_file_content(path) + tmp_tar_name = Base64.encode64(path)[0..20] + tmp_tar_path = "/tmp/#{tmp_tar_name}.tar.gz" + + Minitar.pack(path, Zlib::GzipWriter.new(File.open(tmp_tar_path, 'wb'))) + gzipped_file_size = Pathname.new(tmp_tar_path).size + + if gzipped_file_size > MAX_HOST_VOLUME_GZIP_FILE_SIZE + Uffizzi.ui.say("File/Directory too big by path: #{path}. Gzipped tar archive size is #{gzipped_file_size}") + end + + Base64.encode64(File.binread(tmp_tar_path)) + end + + def prepare_config_files_paths(configs_data) return [] if configs_data.nil? Uffizzi.ui.say("Unsupported type of #{:configs} option") unless configs_data.is_a?(Hash) configs = [] @@ -66,40 +122,42 @@ else Uffizzi.ui.say("Unsupported type of #{:env_file} option") end end - def prepare_services_env_files(services) + def prepare_env_files_paths(services) return [] if services.nil? - services.keys.map do |service| - service_env_files = prepare_service_env_files(services.fetch(service)) - - service_env_files - end + services + .values + .select { |s| s.has_key?('env_file') } + .map { |s| parse_env_file(s['env_file']) } + .flatten + .compact + .uniq end - def prepare_service_env_files(service_data) - env_files_data = [] - service_data.each_pair do |key, value| - key_sym = key.to_sym - if key_sym == :env_file - env_files_data << parse_env_file(value) - end - end - - env_files_data - end - def parse_compose_content_to_object(compose_content) begin compose_data = Psych.safe_load(compose_content, aliases: true) rescue Psych::SyntaxError Uffizzi.ui.say('Invalid compose file') end Uffizzi.ui.say('Unsupported compose file') if compose_data.nil? compose_data + end + + def prepare_host_volumes_paths(services) + return [] if services.nil? + + services + .values + .select { |s| s.has_key?('volumes') } + .map { |s| VolumeParserService.parse(s['volumes']) } + .flatten + .compact + .uniq end end end