require 'instance_agent/plugins/codedeploy/application_specification/script_info' require 'instance_agent/plugins/codedeploy/application_specification/file_info' require 'instance_agent/plugins/codedeploy/application_specification/linux_permission_info' require 'instance_agent/plugins/codedeploy/application_specification/mode_info' module InstanceAgent module Plugins module CodeDeployPlugin module ApplicationSpecification class AppSpecValidationException < Exception; end class ApplicationSpecification attr_reader :version, :os, :hooks, :files, :permissions, :file_exists_behavior def initialize(yaml_hash, opts = {}) unless yaml_hash raise AppSpecValidationException, "The deployment failed because the application specification file was empty. Make sure your AppSpec file defines at minimum the 'version' and 'os' properties." end @version = parse_version(yaml_hash['version']) @os = parse_os(yaml_hash['os']) @hooks = parse_hooks(yaml_hash['hooks'] || {}) @files = parse_files(yaml_hash['files'] || []) @permissions = parse_permissions(yaml_hash['permissions'] || []) @file_exists_behavior = parse_file_exists_behavior(yaml_hash['file_exists_behavior']) end def self.parse(app_spec_string) new(YAML.load(app_spec_string)) end private def supported_versions() [0.0] end private def valid_file_exists_behaviors %w[DISALLOW OVERWRITE RETAIN] end def parse_version(version) unless supported_versions.include?(version) raise AppSpecValidationException, "The deployment failed because an invalid version value (#{version}) was entered in the application specification file. Make sure your AppSpec file specifies \"0.0\" as the version, and then try again." end version end def parse_file_exists_behavior(file_exists_behavior) unless file_exists_behavior.nil? or valid_file_exists_behaviors.include?(file_exists_behavior) raise AppSpecValidationException, "The deployment failed because an invalid file_exists_behavior value (#{file_exists_behavior}) was entered in the application specification file. Make sure your AppSpec file specifies one of #{valid_file_exists_behaviors * ","} as the file_exists_behavior, and then try again." end file_exists_behavior end def supported_oses() InstanceAgent::Platform.util.supported_oses() end def parse_os(os) unless supported_oses.include?(os) raise AppSpecValidationException, "The deployment failed because the application specification file specifies an unsupported operating system (#{os}). Specify either \"linux\" or \"windows\" in the os section of the AppSpec file, and then try again." end os end def parse_files(file_map_hash) files = [] #loop through hash and create fileInfo representations file_map_hash.each do |mapping| files << FileInfo.new(mapping['source'], mapping['destination']) end files end def parse_hooks(hooks_hash) temp_hooks_hash = Hash.new hooks_hash.each_pair do |hook, scripts| # Ignore empty hooks. if scripts current_hook_scripts = [] scripts.each do |script| if (script.has_key?('location') && !script['location'].nil?) current_hook_scripts << InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ScriptInfo.new(script['location'].to_s.strip, { :runas => script.has_key?('runas') && !script['runas'].nil? ? script['runas'].to_s.strip : nil, :sudo => script['sudo'], :timeout => script['timeout'] }) else raise AppSpecValidationException, 'The deployment failed because the application specification file specifies a script with no location value. Specify the location in the hooks section of the AppSpec file, and then try again.' end end temp_hooks_hash[hook] = current_hook_scripts end end temp_hooks_hash end def parse_permissions(permissions_list) permissions = [] #loop through list and create permissionsInfo representations permissions_list.each do |permission| if !permission.has_key?('object') || permission['object'].nil? raise AppSpecValidationException, 'The deployment failed because a permission listed in the application specification file has no object value. Update the permissions section of the AppSpec file, and then try again.' end if @os.eql?('linux') permissions << InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::LinuxPermissionInfo.new(permission['object'].to_s.strip, { :pattern => ('**'.eql?(permission['pattern']) || permission['pattern'].nil?) ? '**' : parse_simple_glob(permission['pattern']), :except => parse_simple_glob_list(permission['except']), :type => parse_type_list(permission['type']), :owner => permission['owner'], :group => permission['group'], :mode => parse_mode(permission['mode']), :acls => parse_acl(permission['acls']), :context => parse_context(permission['context']) }) else raise AppSpecValidationException, 'The deployment failed because the application specification file specifiles file permissions, but the deployment is targeting one or more Windows Server instances. Permissions are supported only for Amazon Linux, Ubuntu Server, and Red Hat Enterprise Linux (RHEL) instances. Update the permissions section of the AppSpec file, and then try again.' end end permissions end #placeholder for parsing globs: we should verify that the glob is only including what we expect. For now just returning it as it is. def parse_simple_glob(glob) glob end def parse_simple_glob_list(glob_list) temp_glob_list = [] (glob_list || []).each do |glob| temp_glob_list << parse_simple_glob(glob) end temp_glob_list end def supported_types ['file', 'directory'] end def parse_type_list(type_list) type_list ||= supported_types type_list.each do |type| if !supported_types.include?(type) raise AppSpecValidationException, "The deployment failed because the application specification filespecifies a permission for an object type not supported for permissions (#{type}). Update the permissions section of the AppSpec file, and then try again." end end type_list end def parse_mode(mode) mode.nil? ? nil : InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ModeInfo.new(mode) end def parse_acl(acl) acl.nil? ? nil : InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::AclInfo.new(acl) end def parse_context(context) context.nil? ? nil : InstanceAgent::Plugins::CodeDeployPlugin::ApplicationSpecification::ContextInfo.new(context) end end end end end end