7
7
import samtranslator .model .eventsources .cloudwatchlogs
8
8
from .api .api_generator import ApiGenerator
9
9
from .api .http_api_generator import HttpApiGenerator
10
- from .s3_utils .uri_parser import construct_s3_location_object
10
+ from .packagetype import ZIP , IMAGE
11
+ from .s3_utils .uri_parser import construct_s3_location_object , construct_image_code_object
11
12
from .tags .resource_tagging import get_tag_list
12
13
from samtranslator .model import PropertyType , SamResourceMacro , ResourceTypeResolver
13
14
from samtranslator .model .apigateway import (
@@ -54,9 +55,11 @@ class SamFunction(SamResourceMacro):
54
55
resource_type = "AWS::Serverless::Function"
55
56
property_types = {
56
57
"FunctionName" : PropertyType (False , one_of (is_str (), is_type (dict ))),
57
- "Handler" : PropertyType (True , is_str ()),
58
- "Runtime" : PropertyType (True , is_str ()),
58
+ "Handler" : PropertyType (False , is_str ()),
59
+ "Runtime" : PropertyType (False , is_str ()),
59
60
"CodeUri" : PropertyType (False , one_of (is_str (), is_type (dict ))),
61
+ "ImageUri" : PropertyType (False , is_str ()),
62
+ "PackageType" : PropertyType (False , is_str ()),
60
63
"InlineCode" : PropertyType (False , one_of (is_str (), is_type (dict ))),
61
64
"DeadLetterQueue" : PropertyType (False , is_type (dict )),
62
65
"Description" : PropertyType (False , is_str ()),
@@ -82,6 +85,7 @@ class SamFunction(SamResourceMacro):
82
85
"VersionDescription" : PropertyType (False , is_str ()),
83
86
"ProvisionedConcurrencyConfig" : PropertyType (False , is_type (dict )),
84
87
"FileSystemConfigs" : PropertyType (False , list_of (is_type (dict ))),
88
+ "ImageConfig" : PropertyType (False , is_type (dict )),
85
89
"CodeSigningConfigArn" : PropertyType (False , is_str ()),
86
90
}
87
91
event_resolver = ResourceTypeResolver (
@@ -406,6 +410,8 @@ def _construct_lambda_function(self):
406
410
lambda_function .Tags = self ._construct_tag_list (self .Tags )
407
411
lambda_function .Layers = self .Layers
408
412
lambda_function .FileSystemConfigs = self .FileSystemConfigs
413
+ lambda_function .ImageConfig = self .ImageConfig
414
+ lambda_function .PackageType = self .PackageType
409
415
410
416
if self .Tracing :
411
417
lambda_function .TracingConfig = {"Mode" : self .Tracing }
@@ -415,6 +421,7 @@ def _construct_lambda_function(self):
415
421
416
422
lambda_function .CodeSigningConfigArn = self .CodeSigningConfigArn
417
423
424
+ self ._validate_package_type (lambda_function )
418
425
return lambda_function
419
426
420
427
def _add_event_invoke_managed_policy (self , dest_config , logical_id , condition , dest_arn ):
@@ -491,6 +498,50 @@ def _construct_role(self, managed_policy_map, event_invoke_policies):
491
498
)
492
499
return execution_role
493
500
501
+ def _validate_package_type (self , lambda_function ):
502
+ """
503
+ Validates Function based on the existence of Package type
504
+ """
505
+ packagetype = lambda_function .PackageType or ZIP
506
+
507
+ if packagetype not in [ZIP , IMAGE ]:
508
+ raise InvalidResourceException (
509
+ lambda_function .logical_id ,
510
+ "PackageType needs to be `{zip}` or `{image}`" .format (zip = ZIP , image = IMAGE ),
511
+ )
512
+
513
+ def _validate_package_type_zip ():
514
+ if not all ([lambda_function .Runtime , lambda_function .Handler ]):
515
+ raise InvalidResourceException (
516
+ lambda_function .logical_id ,
517
+ "Runtime and Handler needs to be present when PackageType is of type `{zip}`" .format (zip = ZIP ),
518
+ )
519
+
520
+ if any ([lambda_function .Code .get ("ImageUri" , False ), lambda_function .ImageConfig ]):
521
+ raise InvalidResourceException (
522
+ lambda_function .logical_id ,
523
+ "ImageUri or ImageConfig cannot be present when PackageType is of type `{zip}`" .format (zip = ZIP ),
524
+ )
525
+
526
+ def _validate_package_type_image ():
527
+ if any ([lambda_function .Handler , lambda_function .Runtime , lambda_function .Layers ]):
528
+ raise InvalidResourceException (
529
+ lambda_function .logical_id ,
530
+ "Runtime, Handler, Layers cannot be present when PackageType is of type `{image}`" .format (
531
+ image = IMAGE
532
+ ),
533
+ )
534
+ if not lambda_function .Code .get ("ImageUri" ):
535
+ raise InvalidResourceException (
536
+ lambda_function .logical_id ,
537
+ "ImageUri needs to be present when PackageType is of type `{image}`" .format (image = IMAGE ),
538
+ )
539
+
540
+ _validate_per_package_type = {ZIP : _validate_package_type_zip , IMAGE : _validate_package_type_image }
541
+
542
+ # Call appropriate validation function based on the package type.
543
+ return _validate_per_package_type [packagetype ]()
544
+
494
545
def _validate_dlq (self ):
495
546
"""Validates whether the DeadLetterQueue LogicalId is validation
496
547
:raise: InvalidResourceException
@@ -577,12 +628,59 @@ def _generate_event_resources(
577
628
return resources
578
629
579
630
def _construct_code_dict (self ):
580
- if self .InlineCode :
631
+ """Constructs Lambda Code Dictionary based on the accepted SAM artifact properties such
632
+ as `InlineCode`, `CodeUri` and `ImageUri` and also raises errors if more than one of them is
633
+ defined. `PackageType` determines which artifacts are considered.
634
+
635
+ :raises InvalidResourceException when conditions on the SAM artifact properties are not met.
636
+ """
637
+ # list of accepted artifacts
638
+ packagetype = self .PackageType or ZIP
639
+ artifacts = {}
640
+
641
+ if packagetype == ZIP :
642
+ artifacts = {"InlineCode" : self .InlineCode , "CodeUri" : self .CodeUri }
643
+ elif packagetype == IMAGE :
644
+ artifacts = {"ImageUri" : self .ImageUri }
645
+
646
+ if packagetype not in [ZIP , IMAGE ]:
647
+ raise InvalidResourceException (self .logical_id , "invalid 'PackageType' : {}" .format (packagetype ))
648
+
649
+ # Inline function for transformation of inline code.
650
+ # It accepts arbitrary argumemnts, because the arguments do not matter for the result.
651
+ def _construct_inline_code (* args , ** kwargs ):
581
652
return {"ZipFile" : self .InlineCode }
582
- elif self .CodeUri :
583
- return construct_s3_location_object (self .CodeUri , self .logical_id , "CodeUri" )
653
+
654
+ # dispatch mechanism per artifact on how it needs to be transformed.
655
+ artifact_dispatch = {
656
+ "InlineCode" : _construct_inline_code ,
657
+ "CodeUri" : construct_s3_location_object ,
658
+ "ImageUri" : construct_image_code_object ,
659
+ }
660
+
661
+ filtered_artifacts = dict (filter (lambda x : x [1 ] != None , artifacts .items ()))
662
+ # There are more than one allowed artifact types present, raise an Error.
663
+ # There are no valid artifact types present, also raise an Error.
664
+ if len (filtered_artifacts ) > 1 or len (filtered_artifacts ) == 0 :
665
+ if packagetype == ZIP and len (filtered_artifacts ) == 0 :
666
+ raise InvalidResourceException (self .logical_id , "Only one of 'InlineCode' or 'CodeUri' can be set." )
667
+ elif packagetype == IMAGE :
668
+ raise InvalidResourceException (self .logical_id , "'ImageUri' must be set." )
669
+
670
+ filtered_keys = [key for key in filtered_artifacts .keys ()]
671
+ # NOTE(sriram-mv): This precedence order is important. It is protect against python2 vs python3
672
+ # dictionary ordering when getting the key values with .keys() on a dictionary.
673
+ # Do not change this precedence order.
674
+ if "InlineCode" in filtered_keys :
675
+ filtered_key = "InlineCode"
676
+ elif "CodeUri" in filtered_keys :
677
+ filtered_key = "CodeUri"
678
+ elif "ImageUri" in filtered_keys :
679
+ filtered_key = "ImageUri"
584
680
else :
585
- raise InvalidResourceException (self .logical_id , "Either 'InlineCode' or 'CodeUri' must be set" )
681
+ raise InvalidResourceException (self .logical_id , "Either 'InlineCode' or 'CodeUri' must be set." )
682
+ dispatch_function = artifact_dispatch [filtered_key ]
683
+ return dispatch_function (artifacts [filtered_key ], self .logical_id , filtered_key )
586
684
587
685
def _construct_version (self , function , intrinsics_resolver , code_sha256 = None ):
588
686
"""Constructs a Lambda Version resource that will be auto-published when CodeUri of the function changes.
0 commit comments