fog-aws 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 53e5c13a8cdab463ca2f7e696ce8f6be7a28e6b7
4
- data.tar.gz: 7c0b522b66731d66b8374cf1cf8d8c8cba679d57
3
+ metadata.gz: 04b74ccb392ce39559dbdd053081d2e304b52957
4
+ data.tar.gz: c5010f4e017481193b8f0e84c17b54a2bb3edc6f
5
5
  SHA512:
6
- metadata.gz: 5c93b6c6b2d5013370a4277bd28002c5b357af8d28846f21c1c44147a1ec4773e15c4ab047795ee5fd718755b66ad153db597be569cbe33e043c930e599e9f01
7
- data.tar.gz: 174a539d140553b296d6ef4beb68f792f6f02a67ca3c17a7d25a76c7d1038aade3807ec7efb7be7e1bb0c479c010ad5e790a9e396bd7fec7895d73de4c620c9e
6
+ metadata.gz: f24cc2d407182a671b21b832ccfbc45fda04f794ac5f6fcb8a117925a86f9df1c82af39d1021d412cf8ca190b98c461807cdeea74322beaaec7af9aafaf33bfa
7
+ data.tar.gz: e6f134e032e570cc657deec822c02621e27e9a7e891adf3967ddb85b2c0fd48883be9fe33de1578d6349226f8279ae8a5563119cf9894f15e96adc781ddc0183
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency 'bundler', '~> 1.6'
23
23
  spec.add_development_dependency 'rake', '~> 10.0'
24
24
  spec.add_development_dependency 'shindo', '~> 0.3'
25
+ spec.add_development_dependency 'rubyzip', '~> 0.9.9'
25
26
 
26
27
  spec.add_dependency 'fog-core', '~> 1.27'
27
28
  spec.add_dependency 'fog-json', '~> 1.0'
@@ -45,6 +45,7 @@ module Fog
45
45
  autoload :Glacier, File.expand_path('../aws/glacier', __FILE__)
46
46
  autoload :IAM, File.expand_path('../aws/iam', __FILE__)
47
47
  autoload :KMS, File.expand_path('../aws/kms', __FILE__)
48
+ autoload :Lambda, File.expand_path('../aws/lambda', __FILE__)
48
49
  autoload :RDS, File.expand_path('../aws/rds', __FILE__)
49
50
  autoload :Redshift, File.expand_path('../aws/redshift', __FILE__)
50
51
  autoload :SES, File.expand_path('../aws/ses', __FILE__)
@@ -70,6 +71,7 @@ module Fog
70
71
  service(:glacier, 'Glacier')
71
72
  service(:iam, 'IAM')
72
73
  service(:kms, 'KMS')
74
+ service(:lambda, 'Lambda')
73
75
  service(:rds, 'RDS')
74
76
  service(:redshift, 'Redshift')
75
77
  service(:ses, 'SES')
@@ -148,16 +150,21 @@ module Fog
148
150
 
149
151
  headers = headers.merge('Host' => options[:host], 'x-amz-date' => date.to_iso8601_basic)
150
152
  headers['x-amz-security-token'] = options[:aws_session_token] if options[:aws_session_token]
153
+ query = options[:query] || {}
151
154
 
152
- body = ''
153
- for key in params.keys.sort
154
- unless (value = params[key]).nil?
155
- body << "#{key}=#{escape(value.to_s)}&"
155
+ if !options[:body]
156
+ body = ''
157
+ for key in params.keys.sort
158
+ unless (value = params[key]).nil?
159
+ body << "#{key}=#{escape(value.to_s)}&"
160
+ end
156
161
  end
162
+ body.chop!
163
+ else
164
+ body = options[:body]
157
165
  end
158
- body.chop!
159
166
 
160
- headers['Authorization'] = options[:signer].sign({:method => options[:method], :headers => headers, :body => body, :query => {}, :path => options[:path]}, date)
167
+ headers['Authorization'] = options[:signer].sign({:method => options[:method], :headers => headers, :body => body, :query => query, :path => options[:path]}, date)
161
168
 
162
169
  return body, headers
163
170
  end
@@ -209,5 +216,10 @@ module Fog
209
216
  end
210
217
  options
211
218
  end
219
+
220
+ def self.json_response?(response)
221
+ return false unless response && response.headers
222
+ response.get_header('Content-Type') =~ %r{application/json}i ? true : false
223
+ end
212
224
  end
213
225
  end
@@ -2,11 +2,32 @@ module Fog
2
2
  module AWS
3
3
  module Errors
4
4
  def self.match_error(error)
5
- matcher = lambda {|s| s.match(/(?:.*<Code>(.*)<\/Code>)(?:.*<Message>(.*)<\/Message>)/m)}
6
- [error.message, error.response.body].each(&Proc.new {|s|
7
- match = matcher.call(s)
8
- return {:code => match[1].split('.').last, :message => match[2]} if match
9
- })
5
+ if !Fog::AWS.json_response?(error.response)
6
+ matchers = [
7
+ lambda {|s| s.match(/(?:.*<Code>(.*)<\/Code>)(?:.*<Message>(.*)<\/Message>)/m)},
8
+ lambda {|s| s.match(/.*<(.+Exception)>(?:.*<Message>(.*)<\/Message>)/m)}
9
+ ]
10
+ [error.message, error.response.body].each(&Proc.new {|s|
11
+ matchers.each do |matcher|
12
+ match = matcher.call(s)
13
+ return {:code => match[1].split('.').last, :message => match[2]} if match
14
+ end
15
+ })
16
+ else
17
+ begin
18
+ full_msg_error = Fog::JSON.decode(error.response.body)
19
+ if (full_msg_error.has_key?('Message') || full_msg_error.has_key?('message')) &&
20
+ error.response.headers.has_key?('x-amzn-ErrorType')
21
+ matched_error = {
22
+ :code => error.response.headers['x-amzn-ErrorType'].split(':').first,
23
+ :message => full_msg_error['Message'] || full_msg_error['message']
24
+ }
25
+ return matched_error
26
+ end
27
+ rescue Fog::JSON::DecodeError => e
28
+ Fog::Logger.warning("Error parsing response json - #{e}")
29
+ end
30
+ end
10
31
  {} # we did not match the message or response body
11
32
  end
12
33
  end
@@ -0,0 +1,195 @@
1
+ module Fog
2
+ module AWS
3
+ class Lambda < Fog::Service
4
+ extend Fog::AWS::CredentialFetcher::ServiceMethods
5
+
6
+ requires :aws_access_key_id, :aws_secret_access_key
7
+ recognizes :host, :path, :port, :scheme, :persistent, :region, :use_iam_profile, :aws_session_token, :aws_credentials_expire_at, :version, :instrumentor, :instrumentor_name
8
+
9
+ request_path 'fog/aws/requests/lambda'
10
+ request :create_function
11
+ request :delete_function
12
+ request :get_function
13
+ request :get_function_configuration
14
+ request :invoke
15
+ request :list_functions
16
+ request :update_function_code
17
+ request :update_function_configuration
18
+
19
+ request :get_policy
20
+ request :add_permission
21
+ request :remove_permission
22
+
23
+ request :create_event_source_mapping
24
+ request :delete_event_source_mapping
25
+ request :get_event_source_mapping
26
+ request :list_event_source_mappings
27
+ request :update_event_source_mapping
28
+
29
+ class Mock
30
+ def self.data
31
+ @data ||= Hash.new do |hash, region|
32
+ hash[region] = Hash.new do |region_hash, key|
33
+ region_hash[key] = {
34
+ :functions => {},
35
+ :permissions => {},
36
+ :event_source_mappings => {}
37
+ }
38
+ end
39
+ end
40
+ end
41
+
42
+ attr_reader :region
43
+ attr_reader :account_id
44
+ attr_reader :aws_access_key_id
45
+
46
+ def initialize(options={})
47
+ @region = options[:region] || 'us-east-1'
48
+ @aws_access_key_id = options[:aws_access_key_id]
49
+ @account_id = Fog::AWS::Mock.owner_id
50
+ @module = "lambda"
51
+
52
+ unless ['ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2', 'eu-central-1', 'eu-west-1', 'us-east-1', 'us-west-1', 'us-west-2', 'sa-east-1'].include?(@region)
53
+ raise ArgumentError, "Unknown region: #{@region.inspect}"
54
+ end
55
+ end
56
+
57
+ def data
58
+ self.class.data[@region][@aws_access_key_id]
59
+ end
60
+
61
+ def reset_data
62
+ self.class.data[@region].delete(@aws_access_key_id)
63
+ end
64
+ end
65
+
66
+ class Real
67
+ include Fog::AWS::CredentialFetcher::ConnectionMethods
68
+ # Initialize connection to Lambda
69
+ #
70
+ # ==== Notes
71
+ # options parameter must include values for :aws_access_key_id and
72
+ # :aws_secret_access_key in order to create a connection
73
+ #
74
+ # ==== Examples
75
+ # lambda = Lambda.new(
76
+ # :aws_access_key_id => your_aws_access_key_id,
77
+ # :aws_secret_access_key => your_aws_secret_access_key
78
+ # )
79
+ #
80
+ # ==== Parameters
81
+ # * options<~Hash> - config arguments for connection. Defaults to {}.
82
+ #
83
+ # ==== Returns
84
+ # * Lambda object with connection to AWS.
85
+ def initialize(options={})
86
+ @use_iam_profile = options[:use_iam_profile]
87
+ @connection_options = options[:connection_options] || {}
88
+ @instrumentor = options[:instrumentor]
89
+ @instrumentor_name = options[:instrumentor_name] || 'fog.aws.lambda'
90
+
91
+ options[:region] ||= 'us-east-1'
92
+ @region = options[:region]
93
+ @host = options[:host] || "lambda.#{options[:region]}.amazonaws.com"
94
+
95
+ @path = options[:path] || '/'
96
+ @persistent = options[:persistent] || false
97
+ @port = options[:port] || 443
98
+ @scheme = options[:scheme] || 'https'
99
+ @version = options[:version] || '2015-03-31'
100
+ @connection = Fog::Core::Connection.new("#{@scheme}://#{@host}:#{@port}#{@path}", @persistent, @connection_options)
101
+
102
+ setup_credentials(options)
103
+ end
104
+
105
+ attr_reader :region
106
+
107
+ def reload
108
+ @connection.reset
109
+ end
110
+
111
+ private
112
+
113
+ def setup_credentials(options)
114
+ @aws_access_key_id = options[:aws_access_key_id]
115
+ @aws_secret_access_key = options[:aws_secret_access_key]
116
+ @aws_session_token = options[:aws_session_token]
117
+ @aws_credentials_expire_at = options[:aws_credentials_expire_at]
118
+
119
+ @signer = Fog::AWS::SignatureV4.new( @aws_access_key_id, @aws_secret_access_key, @region, 'lambda')
120
+ end
121
+
122
+ def request(params)
123
+ refresh_credentials_if_expired
124
+
125
+ idempotent = params.delete(:idempotent)
126
+ parser = params.delete(:parser)
127
+ request_path = "/#{@version}#{params.delete(:path)}"
128
+ query = params.delete(:query) || {}
129
+ method = params.delete(:method) || 'POST'
130
+ expects = params.delete(:expects) || 200
131
+ headers = { 'Content-Type' => 'application/json' }
132
+
133
+ headers.merge!(params[:headers] || {})
134
+
135
+ body, headers = AWS.signed_params_v4(
136
+ params,
137
+ headers,
138
+ {
139
+ :method => method,
140
+ :aws_session_token => @aws_session_token,
141
+ :signer => @signer,
142
+ :host => @host,
143
+ :path => request_path,
144
+ :port => @port,
145
+ :query => query,
146
+ :body => params[:body]
147
+ }
148
+ )
149
+
150
+ if @instrumentor
151
+ @instrumentor.instrument("#{@instrumentor_name}.request", params) do
152
+ _request(method, request_path, query, body, headers, expects, idempotent, parser)
153
+ end
154
+ else
155
+ _request(method, request_path, query, body, headers, expects, idempotent, parser)
156
+ end
157
+ end
158
+
159
+ def _request(method, path, query, body, headers, expects, idempotent, parser=nil)
160
+ response = process_response(@connection.request({
161
+ :path => path,
162
+ :query => query,
163
+ :body => body,
164
+ :expects => expects,
165
+ :idempotent => idempotent,
166
+ :headers => headers,
167
+ :method => method
168
+ }), parser)
169
+ rescue Excon::Errors::HTTPStatusError => error
170
+ match = Fog::AWS::Errors.match_error(error)
171
+ raise if match.empty?
172
+ raise Fog::AWS::Lambda::Error.slurp(error,
173
+ "#{match[:code]} => #{match[:message]}")
174
+ end
175
+
176
+ def process_response(response, parser)
177
+ if response &&
178
+ response.body &&
179
+ response.body.is_a?(String) &&
180
+ !response.body.strip.empty? &&
181
+ Fog::AWS.json_response?(response)
182
+ begin
183
+ response.body = Fog::JSON.decode(response.body)
184
+ response.body = parser.process(response.body) if parser
185
+ rescue Fog::JSON::DecodeError => e
186
+ Fog::Logger.warning("Error parsing response json - #{e}")
187
+ response.body = {}
188
+ end
189
+ end
190
+ response
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,39 @@
1
+ module Fog
2
+ module AWS
3
+ module Parsers
4
+ module Lambda
5
+ class Base
6
+ def process(body)
7
+ body.inject({}) { |h, (k, v)| h[k] = rules(k, v); h }
8
+ end
9
+
10
+ private
11
+
12
+ def rules(key, value)
13
+ case value
14
+ when Hash
15
+ process(value)
16
+ when Array
17
+ value.map { |i| process(i) }
18
+ else
19
+ case key
20
+ when 'LastModified'
21
+ Time.parse(value)
22
+ when 'Policy', 'Statement'
23
+ begin
24
+ Fog::JSON.decode(value)
25
+ rescue Fog::JSON::DecodeError => e
26
+ Fog::Logger.warning("Error parsing response json - #{e}")
27
+ {}
28
+ end
29
+ else
30
+ value
31
+ end
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,100 @@
1
+ module Fog
2
+ module AWS
3
+ class Lambda
4
+ class Real
5
+ require 'fog/aws/parsers/lambda/base'
6
+
7
+ # Adds a permission to the access policy associated with the specified AWS Lambda function.
8
+ # http://docs.aws.amazon.com/lambda/latest/dg/API_AddPermission.html
9
+ # ==== Parameters
10
+ # * FunctionName <~String> - Name of the Lambda function whose access policy you are updating by adding a new permission.
11
+ # * Action <~String> - AWS Lambda action you want to allow in this statement.
12
+ # * Principal <~String> - principal who is getting this permission.
13
+ # * SourceAccount <~String> - AWS account ID (without a hyphen) of the source owner.
14
+ # * SourceArn <~String> - Amazon Resource Name (ARN) of the source resource to assign permissions.
15
+ # * StatemendId. <~String> - unique statement identifier.
16
+ # ==== Returns
17
+ # * response<~Excon::Response>:
18
+ # * body<~Hash>:
19
+ # * 'Statement' <~Hash> - permission statement you specified in the request.
20
+ def add_permission(params={})
21
+ function_name = params.delete('FunctionName')
22
+ action = params.delete('Action')
23
+ principal = params.delete('Principal')
24
+ source_account = params.delete('SourceAccount')
25
+ source_arn = params.delete('SourceArn')
26
+ sid = params.delete('StatementId')
27
+
28
+ permission = {
29
+ 'Action' => action,
30
+ 'Principal' => principal,
31
+ 'StatementId' => sid
32
+ }
33
+ permission['SourceAccount'] = source_account if source_account
34
+ permission['SourceArn'] = source_arn if source_arn
35
+
36
+ request({
37
+ :method => 'POST',
38
+ :path => "/functions/#{function_name}/versions/HEAD/policy",
39
+ :expects => 201,
40
+ :body => Fog::JSON.encode(permission),
41
+ :parser => Fog::AWS::Parsers::Lambda::Base.new
42
+ }.merge(params))
43
+ end
44
+ end
45
+
46
+ class Mock
47
+ def add_permission(params={})
48
+ function_id = params.delete('FunctionName')
49
+ function = self.get_function_configuration(
50
+ 'FunctionName' => function_id
51
+ ).body
52
+ function_arn = function['FunctionArn']
53
+
54
+ action = params.delete('Action')
55
+ principal = params.delete('Principal')
56
+ source_account = params.delete('SourceAccount')
57
+ source_arn = params.delete('SourceArn')
58
+ sid = params.delete('StatementId')
59
+
60
+ if action.nil? || action.empty?
61
+ message = 'Action cannot be blank'
62
+ raise Fog::AWS::Lambda::Error, message
63
+ end
64
+
65
+ if principal.nil? || principal.empty?
66
+ message = 'Principal cannot be blank'
67
+ raise Fog::AWS::Lambda::Error, message
68
+ end
69
+
70
+ if sid.nil? || sid.empty?
71
+ message = 'Sid cannot be blank'
72
+ raise Fog::AWS::Lambda::Error, message
73
+ end
74
+
75
+ statement = {
76
+ 'Action' => [action],
77
+ 'Principal' => { 'Service' => principal },
78
+ 'Sid' => sid,
79
+ 'Resource' => function_arn,
80
+ 'Effect' => 'Allow'
81
+ }
82
+ if source_arn
83
+ statement['Condition'] = {}
84
+ statement['Condition']['ArnLike'] = {
85
+ 'AWS:SourceArn' => source_arn
86
+ }
87
+ end
88
+
89
+ self.data[:permissions][function_arn] ||= []
90
+ self.data[:permissions][function_arn] << statement
91
+
92
+ response = Excon::Response.new
93
+ response.status = 201
94
+ response.body = { 'Statement' => statement }
95
+ response
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end