require_relative 'service_extender' require_relative 'validator' require_relative 'validator_v2' require 'kontena/util' module Kontena::Cli::Apps module YAML class Reader include Kontena::Util attr_reader :yaml, :file, :errors, :notifications def initialize(file, skip_validation = false) @file = file @errors = [] @notifications = [] @skip_validation = skip_validation load_yaml validate unless skip_validation? end ## # @param [String] service_name # @return [Hash] def execute(service_name = nil) result = {} Dir.chdir(File.dirname(File.expand_path(file))) do result[:version] = yaml['version'] || '1' result[:name] = yaml['name'] result[:errors] = errors result[:notifications] = notifications result[:services] = errors.count == 0 ? parse_services(service_name) : {} end result end def stack_name yaml['name'] if v2? end ## # @return [true|false] def v2? yaml['version'].to_s == '2' end private def load_yaml content = File.read(File.expand_path(file)) content = content % { project: ENV['project'], grid: ENV['grid'] } interpolate(content) replace_dollar_dollars(content) begin @yaml = ::YAML.safe_load(content) rescue Psych::SyntaxError => ex raise ex.class, "Error while parsing #{file} : #{ex.message}" end end # @return [Array] array of validation errors def validate result = validator.validate(yaml) store_failures(result) result end def skip_validation? @skip_validation == true end def store_failures(data) errors << { file => data[:errors] } unless data[:errors].empty? notifications << { file => data[:notifications] } unless data[:notifications].empty? end # @return [Kontena::Cli::Apps::YAML::Validator] def validator if @validator.nil? validator_klass = v2? ? YAML::ValidatorV2 : YAML::Validator @validator = validator_klass.new end @validator end ## # @param [String] service_name - optional service to parse # @return [Hash] def parse_services(service_name = nil) if service_name.nil? services.each do |name, config| services[name] = process_config(config) end services else raise ("Service '#{service_name}' not found in #{file}") unless services.key?(service_name) process_config(services[service_name]) end end # @param [Hash] service_config def process_config(service_config) normalize_env_vars(service_config) merge_env_vars(service_config) expand_build_context(service_config) normalize_build_args(service_config) service_config = extend_config(service_config) if service_config.key?('extends') service_config end # @return [Hash] - services from YAML file def services if v2? yaml['services'] else yaml end end ## # @param [String] text - content of YAML file def interpolate(text) text.gsub!(/(?