Class: Jets::Cfn::Resource::Lambda::Function

Inherits:
Base
  • Object
show all
Includes:
Environment
Defined in:
lib/jets/cfn/resource/lambda/function/controller.rb,
lib/jets/cfn/resource/lambda/function.rb,
lib/jets/cfn/resource/lambda/function/environment.rb

Overview

Handles one_lambda_per_controller

Direct Known Subclasses

Controller, One::Function

Defined Under Namespace

Modules: Environment Classes: Controller

Constant Summary collapse

MAX_FUNCTION_NAME_SIZE =
64

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Environment

#env_properties, #environment, #jets_env

Methods inherited from Base

#attributes, #logical_id, #outputs, #parameters, #permission, #properties, #replacer, #standarize, #template, truncate_id, #type

Methods included from Util::Camelize

#camelize

Constructor Details

#initialize(definition) ⇒ Function

Returns a new instance of Function.



6
7
8
9
# File 'lib/jets/cfn/resource/lambda/function.rb', line 6

def initialize(definition)
  @definition = definition
  @app_class = definition.class_name.to_s
end

Class Method Details

.default_runtimesObject

Also used by jets/stack/main/dsl/lambda.rb



179
180
181
182
183
184
185
# File 'lib/jets/cfn/resource/lambda/function.rb', line 179

def self.default_runtimes
  {
    node: "nodejs18.x",
    python: "python3.10",
    ruby: Jets.ruby_runtime,
  }
end

Instance Method Details

#assign_iam_role?(klass) ⇒ Boolean

The IAM Role is built in the same nested template but determination of whether or not to assign the IAM Role is determined by the inheritance. The merging of permissions is already handled by Resource::Iam::*Role classes. This also avoids having to pass Application IAM role around.

Returns:

  • (Boolean)


91
92
93
94
95
96
97
98
99
100
101
# File 'lib/jets/cfn/resource/lambda/function.rb', line 91

def assign_iam_role?(klass)
  assign = false
  while klass && klass != Object
    if klass&.build_class_iam?
      assign = true
      break
    end
    klass = klass.superclass
  end
  assign
end

#class_propertiesObject

Class properties example:

class PostsController < ApplicationController
  class_timeout 22
  ...
end

Also handles iam policy override at the class level. Example:

class_iam_policy("logs:*")


74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/jets/cfn/resource/lambda/function.rb', line 74

def class_properties
  # klass is PostsController, HardJob, GameRule, Hello or HelloFunction
  klass = Jets::Klass.from_definition(@definition)

  class_properties = lookup_class_properties(klass)
  if assign_iam_role?(klass)
    iam_policy = Jets::Cfn::Resource::Iam::ClassRole.new(klass)
    class_properties[:Role] = "!GetAtt #{iam_policy.logical_id}.Arn"
  end

  camelize(class_properties)
end

#code_s3_keyObject



214
215
216
217
# File 'lib/jets/cfn/resource/lambda/function.rb', line 214

def code_s3_key
  checksum = Jets::Builders::Md5.checksums["stage/code"]
  "jets/code/code-#{checksum}.zip" # s3_key
end

#combined_propertiesObject



31
32
33
34
35
36
37
# File 'lib/jets/cfn/resource/lambda/function.rb', line 31

def combined_properties
  props = env_properties
    .deep_merge(global_properties)
    .deep_merge(class_properties)
    .deep_merge(function_properties)
  finalize_properties!(props)
end

#default_descriptionObject

Example values:

@app_class: Admin/PagesController
@definition.meth: index

Returns:

Admin/PagesController

or

Admin/PagesController#index


254
255
256
257
258
259
260
# File 'lib/jets/cfn/resource/lambda/function.rb', line 254

def default_description
  if one_lambda_per_controller?
    "#{@app_class}"
  else
    "#{@app_class}##{@definition.meth}"
  end
end

#default_handlerObject



187
188
189
190
191
192
193
194
# File 'lib/jets/cfn/resource/lambda/function.rb', line 187

def default_handler
  map = {
    node: @definition.full_handler(:handler), # IE: handlers/controllers/posts/show.handler
    python: @definition.full_handler(:lambda_handler), # IE: handlers/controllers/posts/show.lambda_handler
    ruby: handler, # IE: handlers/controllers/posts_controllers.index
  }
  map[@definition.lang]
end

#default_runtimeObject



174
175
176
# File 'lib/jets/cfn/resource/lambda/function.rb', line 174

def default_runtime
  self.class.default_runtimes[@definition.lang]
end

#definitionObject



11
12
13
14
15
16
17
18
# File 'lib/jets/cfn/resource/lambda/function.rb', line 11

def definition
  {
    function_logical_id => {
      Type: "AWS::Lambda::Function",
      Properties: combined_properties
    }
  }
end

#finalize_properties!(props) ⇒ Object

Properties managed by Jets merged with finality.



143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/jets/cfn/resource/lambda/function.rb', line 143

def finalize_properties!(props)
  handler = full_handler(props)
  runtime = get_runtime(props)
  description = get_descripton(props)
  managed = {
    Handler: handler,
    Runtime: runtime,
    Description: description,
  }
  managed[:FunctionName] = function_name if function_name
  layers = get_layers(runtime)
  managed[:Layers] = layers if layers
  props.merge!(managed)
end

#full_handler(props) ⇒ Object

Ensure that the handler path is normalized.



206
207
208
209
210
211
212
# File 'lib/jets/cfn/resource/lambda/function.rb', line 206

def full_handler(props)
  if props[:Handler]
    handler_value(props[:Handler])
  else
    default_handler
  end
end

#function_logical_idObject

Examples:

one_lambda_per_controller PostsControllerLambdaHandlerLambdaFunction
one_lambda_per_method     PostsControllerIndexLambdaFunction


23
24
25
# File 'lib/jets/cfn/resource/lambda/function.rb', line 23

def function_logical_id
  "{namespace}LambdaFunction"
end

#function_nameObject

Examples:

"#{Jets.project_namespace}-sleep_job-perform"
"demo-dev-sleep_job-perform"


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/jets/cfn/resource/lambda/function.rb', line 222

def function_name
  return if ENV['JETS_RESET'] # reset mode, let CloudFormation manage the function name
  # Example values:
  #   @app_class: admin/pages_controller
  #   @definition.meth: index
  #   method: admin/pages_controller
  #   method: admin-pages_controller-index

  method = @app_class.underscore
  method = method.gsub('/','-').gsub(/[^0-9a-z\-_]/i, '')
  unless one_lambda_per_controller?
    method += "-#{@definition.meth}"
  end
  function_name = "#{Jets.project_namespace}-#{method}"

  # Returns nil if function name is too long.
  # CloudFormation will managed the the function name in this case.
  # A pretty function name won't be generated but the deploy will be successful.
  function_name.size > MAX_FUNCTION_NAME_SIZE ? nil : function_name
end

#function_propertiesObject

Function properties example:

class PostsController < ApplicationController

timeout 18
def index
  ...
end

Also handles iam policy override at the function level. Example:

iam_policy("ec2:*")
def new
  render json: params.merge(action: "new")
end


133
134
135
136
137
138
139
140
# File 'lib/jets/cfn/resource/lambda/function.rb', line 133

def function_properties
  properties = @definition.properties
  if @definition.build_function_iam?
    iam_policy = Jets::Cfn::Resource::Iam::FunctionRole.new(@definition)
    properties[:Role] = "!GetAtt #{iam_policy.logical_id}.Arn"
  end
  camelize(properties)
end

#get_descripton(props) ⇒ Object



243
244
245
# File 'lib/jets/cfn/resource/lambda/function.rb', line 243

def get_descripton(props)
  props[:Description] || default_description
end

#get_layers(runtime) ⇒ Object



158
159
160
161
162
163
# File 'lib/jets/cfn/resource/lambda/function.rb', line 158

def get_layers(runtime)
  return nil unless runtime =~ /^ruby/ || runtime =~ /^provided/
  return Jets.config.lambda.layers if Jets.config.pro.disable

  ["!Ref GemLayer"] + Jets.config.lambda.layers
end

#get_runtime(props) ⇒ Object



165
166
167
168
169
170
171
172
# File 'lib/jets/cfn/resource/lambda/function.rb', line 165

def get_runtime(props)
  # Only allow custom runtime for ruby so polymorphic support works for python and node
  if @definition.lang == :ruby
    props[:Runtime] || default_runtime
  else
    default_runtime
  end
end

#global_propertiesObject

Global properties example: jets defaults are in jets/default/application.rb. Your application’s default config/application.rb then get used. Example:

Jets.application.configure do
  config.function = ActiveSupport::OrderedOptions.new
  config.function.timeout = 30
  config.function.runtime = "nodejs8.10"
  config.function.memory_size = 1536
end


49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/jets/cfn/resource/lambda/function.rb', line 49

def global_properties
  baseline = {
    Code: {
      S3Bucket: "!Ref S3Bucket",
      S3Key: code_s3_key
    },
    Role: "!Ref IamRole",
    Environment: { Variables: environment },
  }

  application_config = camelize(Jets.application.config.function.to_h)
  baseline.merge(application_config)
end

#handlerObject



196
197
198
# File 'lib/jets/cfn/resource/lambda/function.rb', line 196

def handler
  handler_value(@definition.meth)  # IE: handlers/controllers/posts_controllers.index
end

#handler_value(meth) ⇒ Object

Used for node-shim also



201
202
203
# File 'lib/jets/cfn/resource/lambda/function.rb', line 201

def handler_value(meth)
  "handlers/#{@definition.type.pluralize}/#{@app_class.underscore}.#{meth}"
end

#lookup_class_properties(klass) ⇒ Object

Accounts for inherited class_properties



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/jets/cfn/resource/lambda/function.rb', line 104

def lookup_class_properties(klass)
  all_classes = []
  while klass != Object
    all_classes << klass
    klass = klass.superclass
  end
  class_properties = {}
  # Go back down class heirachry top to down
  all_classes.reverse.each do |k|
    class_properties.merge!(k.class_properties)
  end
  class_properties
end

#one_lambda_per_controller?Boolean

Returns:

  • (Boolean)


262
263
264
# File 'lib/jets/cfn/resource/lambda/function.rb', line 262

def one_lambda_per_controller?
  Jets.one_lambda_per_controller? && @app_class.to_s.ends_with?("Controller")
end

#replacementsObject



27
28
29
# File 'lib/jets/cfn/resource/lambda/function.rb', line 27

def replacements
  @definition.replacements # has namespace replacement
end