lib/instance_agent/plugins/codedeploy/application_specification/application_specification.rb
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