diff --git a/samcli/lib/build/build_strategy.py b/samcli/lib/build/build_strategy.py index 5a10af9f30..c59b3078b7 100644 --- a/samcli/lib/build/build_strategy.py +++ b/samcli/lib/build/build_strategy.py @@ -24,6 +24,7 @@ DEFAULT_DEPENDENCIES_DIR, ) from samcli.lib.build.exceptions import MissingBuildMethodException +from samcli.lib.build.utils import warn_on_invalid_architecture LOG = logging.getLogger(__name__) @@ -214,6 +215,9 @@ def build_single_layer_definition(self, layer_definition: LayerBuildDefinition) f"Please provide BuildMethod in Metadata." ) + if layer.build_method == "makefile": + warn_on_invalid_architecture(layer_definition) + single_build_dir = layer.get_build_dir(self._build_dir) # when a layer is passed here, it is ZIP function, codeuri and runtime are not None # codeuri and compatible_runtimes are not None @@ -326,6 +330,7 @@ def build_single_layer_definition(self, layer_definition: LayerBuildDefinition) """ Builds single layer definition with caching """ + code_dir = str(pathlib.Path(self._base_dir, cast(str, layer_definition.codeuri)).resolve()) source_hash = dir_checksum(code_dir, ignore_list=[".aws-sam"], hash_generator=hashlib.sha256()) cache_function_dir = pathlib.Path(self._cache_dir, layer_definition.uuid) diff --git a/samcli/lib/build/utils.py b/samcli/lib/build/utils.py index 534fb58aa1..c4941f369a 100644 --- a/samcli/lib/build/utils.py +++ b/samcli/lib/build/utils.py @@ -6,10 +6,29 @@ from samcli.commands.local.lib.exceptions import OverridesNotWellDefinedError from samcli.lib.providers.provider import Function, LayerVersion +from samcli.lib.build.build_graph import LayerBuildDefinition +from samcli.lib.utils.architecture import X86_64, ARM64 LOG = logging.getLogger(__name__) +def valid_architecture(architecture: str) -> bool: + return architecture in [X86_64, ARM64] + + +def warn_on_invalid_architecture(layer_definition: LayerBuildDefinition) -> None: + """ + Validate the BuildArchitecture of a LambdaLayer. + + Prints corresponding LOG warning if something is invalid. + """ + layer_architecture = layer_definition.architecture + layer_id = layer_definition.layer.layer_id + + if not valid_architecture(layer_architecture): + LOG.warning("WARNING: `%s` in Layer `%s` is not a valid architecture.", layer_architecture, layer_id) + + def _make_env_vars( resource: Union[Function, LayerVersion], file_env_vars: Dict, inline_env_vars: Optional[Dict] ) -> Dict: diff --git a/samcli/lib/providers/provider.py b/samcli/lib/providers/provider.py index a40a5d3632..59e3f98f7f 100644 --- a/samcli/lib/providers/provider.py +++ b/samcli/lib/providers/provider.py @@ -246,8 +246,8 @@ def __init__( if "BuildArchitecture" not in metadata: LOG.warning( - f"WARNING: No BuildArchitecture specifed in Layer `{self._custom_layer_id}`" - + " Metadata. Defaulting to x86_64." + "WARNING: No BuildArchitecture specifed in Layer `%s`" + " Metadata. Defaulting to x86_64.", + self._custom_layer_id, ) self._build_architecture = cast(str, metadata.get("BuildArchitecture", X86_64)) @@ -255,8 +255,9 @@ def __init__( if self._compatible_architectures and self._build_architecture not in self._compatible_architectures: LOG.warning( - f"WARNING: Layer `{self._custom_layer_id}` has BuildArchitecture `{self._build_architecture}`," - + " which is not listed in CompatibleArchitectures." + "WARNING: Layer `%s` has BuildArchitecture `%s`," + " which is not listed in CompatibleArchitectures.", + self._custom_layer_id, + self._build_architecture, ) self._skip_build = bool(metadata.get(SAM_METADATA_SKIP_BUILD_KEY, False)) diff --git a/samcli/local/docker/lambda_build_container.py b/samcli/local/docker/lambda_build_container.py index 8a18e45ebd..4a62b4807e 100644 --- a/samcli/local/docker/lambda_build_container.py +++ b/samcli/local/docker/lambda_build_container.py @@ -10,12 +10,23 @@ from uuid import uuid4 from samcli.commands._utils.experimental import get_enabled_experimental_flags +from samcli.commands.exceptions import UserException +from samcli.lib.build.utils import valid_architecture +from samcli.lib.utils.architecture import ARM64, X86_64 from samcli.lib.utils.lambda_builders import patch_runtime from samcli.local.docker.container import Container LOG = logging.getLogger(__name__) +class InvalidArchitectureForImage(UserException): + """ + Raised when architecture that is provided for the image is invalid + """ + + pass + + class LambdaBuildContainer(Container): """ Class to manage Build containers that are capable of building AWS Lambda functions. @@ -298,4 +309,8 @@ def get_image_tag(architecture): str Image tag """ + if not valid_architecture(architecture): + raise InvalidArchitectureForImage( + f"'{architecture}' is not a valid architecture, it should be either '{X86_64}' or '{ARM64}'" + ) return f"{LambdaBuildContainer._IMAGE_TAG}-{architecture}" diff --git a/schema/samcli.json b/schema/samcli.json index d49cbee080..d6b0c219f8 100644 --- a/schema/samcli.json +++ b/schema/samcli.json @@ -23,7 +23,7 @@ "properties": { "parameters": { "title": "Parameters for the init command", - "description": "Available parameters for the init command:\n* no_interactive:\nDisable interactive prompting for init parameters. (fail if any required values are missing)\n* architecture:\nArchitectures for Lambda functions.\n\nArchitectures: ['arm64', 'x86_64']\n* location:\nTemplate location (git, mercurial, http(s), zip, path).\n* runtime:\nLambda runtime for application.\n\nRuntimes: dotnet6, go1.x, java17, java11, java8.al2, java8, nodejs18.x, nodejs16.x, nodejs14.x, nodejs12.x, provided, provided.al2, python3.9, python3.8, python3.7, python3.11, python3.10, ruby3.2, ruby2.7\n* package_type:\nLambda deployment package type.\n\nPackage Types: Zip, Image\n* base_image:\nLambda base image for deploying IMAGE based package type.\n\nBase images: amazon/dotnet6-base, amazon/go-provided.al2-base, amazon/go1.x-base, amazon/java11-base, amazon/java17-base, amazon/java8-base, amazon/java8.al2-base, amazon/nodejs12.x-base, amazon/nodejs14.x-base, amazon/nodejs16.x-base, amazon/nodejs18.x-base, amazon/python3.10-base, amazon/python3.11-base, amazon/python3.7-base, amazon/python3.8-base, amazon/python3.9-base, amazon/ruby2.7-base, amazon/ruby3.2-base\n* dependency_manager:\nDependency manager for Lambda runtime.\n\nDependency managers: bundler, cli-package, gradle, maven, mod, npm, pip\n* output_dir:\nDirectory to initialize AWS SAM application.\n* name:\nName of AWS SAM Application.\n* app_template:\nIdentifier of the managed application template to be used. Alternatively, run '$sam init' without options for an interactive workflow.\n* no_input:\nDisable Cookiecutter prompting and accept default values defined in the cookiecutter config.\n* extra_context:\nOverride custom parameters in the template's cookiecutter.json configuration e.g. {\"customParam1\": \"customValue1\", \"customParam2\":\"customValue2\"}\n* tracing:\nEnable AWS X-Ray tracing for application.\n* application_insights:\nEnable CloudWatch Application Insights monitoring for application.\n* structured_logging:\nEnable Structured Logging for application.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "description": "Available parameters for the init command:\n* no_interactive:\nDisable interactive prompting for init parameters. (fail if any required values are missing)\n* architecture:\nArchitectures for Lambda functions.\n\nArchitectures: ['arm64', 'x86_64']\n* location:\nTemplate location (git, mercurial, http(s), zip, path).\n* runtime:\nLambda runtime for application.\n\nRuntimes: dotnet6, go1.x, java21, java17, java11, java8.al2, java8, nodejs20.x, nodejs18.x, nodejs16.x, nodejs14.x, nodejs12.x, provided, provided.al2, provided.al2023, python3.9, python3.8, python3.7, python3.12, python3.11, python3.10, ruby3.2, ruby2.7\n* package_type:\nLambda deployment package type.\n\nPackage Types: Zip, Image\n* base_image:\nLambda base image for deploying IMAGE based package type.\n\nBase images: amazon/dotnet6-base, amazon/go-provided.al2-base, amazon/go-provided.al2023-base, amazon/go1.x-base, amazon/java11-base, amazon/java17-base, amazon/java21-base, amazon/java8-base, amazon/java8.al2-base, amazon/nodejs12.x-base, amazon/nodejs14.x-base, amazon/nodejs16.x-base, amazon/nodejs18.x-base, amazon/nodejs20.x-base, amazon/python3.10-base, amazon/python3.11-base, amazon/python3.12-base, amazon/python3.7-base, amazon/python3.8-base, amazon/python3.9-base, amazon/ruby2.7-base, amazon/ruby3.2-base\n* dependency_manager:\nDependency manager for Lambda runtime.\n\nDependency managers: bundler, cli-package, gradle, maven, mod, npm, pip\n* output_dir:\nDirectory to initialize AWS SAM application.\n* name:\nName of AWS SAM Application.\n* app_template:\nIdentifier of the managed application template to be used. Alternatively, run '$sam init' without options for an interactive workflow.\n* no_input:\nDisable Cookiecutter prompting and accept default values defined in the cookiecutter config.\n* extra_context:\nOverride custom parameters in the template's cookiecutter.json configuration e.g. {\"customParam1\": \"customValue1\", \"customParam2\":\"customValue2\"}\n* tracing:\nEnable AWS X-Ray tracing for application.\n* application_insights:\nEnable CloudWatch Application Insights monitoring for application.\n* structured_logging:\nEnable Structured Logging for application.\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "no_interactive": { @@ -48,18 +48,20 @@ "runtime": { "title": "runtime", "type": "string", - "description": "Lambda runtime for application.\n\nRuntimes: dotnet6, go1.x, java17, java11, java8.al2, java8, nodejs18.x, nodejs16.x, nodejs14.x, nodejs12.x, provided, provided.al2, provided.al2023, python3.9, python3.8, python3.7, python3.11, python3.10, ruby3.2, ruby2.7", + "description": "Lambda runtime for application.\n\nRuntimes: dotnet6, go1.x, java21, java17, java11, java8.al2, java8, nodejs20.x, nodejs18.x, nodejs16.x, nodejs14.x, nodejs12.x, provided, provided.al2, provided.al2023, python3.9, python3.8, python3.7, python3.12, python3.11, python3.10, ruby3.2, ruby2.7", "enum": [ "dotnet6", "go1.x", "java11", "java17", + "java21", "java8", "java8.al2", "nodejs12.x", "nodejs14.x", "nodejs16.x", "nodejs18.x", + "nodejs20.x", "provided", "provided.al2", "provided.al2023", @@ -85,7 +87,7 @@ "base_image": { "title": "base_image", "type": "string", - "description": "Lambda base image for deploying IMAGE based package type.\n\nBase images: amazon/dotnet6-base, amazon/go-provided.al2-base, amazon/go-provided.al2023-base, amazon/go1.x-base, amazon/java11-base, amazon/java17-base, amazon/java8-base, amazon/java8.al2-base, amazon/nodejs12.x-base, amazon/nodejs14.x-base, amazon/nodejs16.x-base, amazon/nodejs18.x-base, amazon/python3.10-base, amazon/python3.11-base, amazon/python3.7-base, amazon/python3.8-base, amazon/python3.9-base, amazon/ruby2.7-base, amazon/ruby3.2-base", + "description": "Lambda base image for deploying IMAGE based package type.\n\nBase images: amazon/dotnet6-base, amazon/go-provided.al2-base, amazon/go-provided.al2023-base, amazon/go1.x-base, amazon/java11-base, amazon/java17-base, amazon/java21-base, amazon/java8-base, amazon/java8.al2-base, amazon/nodejs12.x-base, amazon/nodejs14.x-base, amazon/nodejs16.x-base, amazon/nodejs18.x-base, amazon/nodejs20.x-base, amazon/python3.10-base, amazon/python3.11-base, amazon/python3.12-base, amazon/python3.7-base, amazon/python3.8-base, amazon/python3.9-base, amazon/ruby2.7-base, amazon/ruby3.2-base", "enum": [ "amazon/dotnet6-base", "amazon/go-provided.al2-base", @@ -93,12 +95,14 @@ "amazon/go1.x-base", "amazon/java11-base", "amazon/java17-base", + "amazon/java21-base", "amazon/java8-base", "amazon/java8.al2-base", "amazon/nodejs12.x-base", "amazon/nodejs14.x-base", "amazon/nodejs16.x-base", "amazon/nodejs18.x-base", + "amazon/nodejs20.x-base", "amazon/python3.10-base", "amazon/python3.11-base", "amazon/python3.12-base", @@ -2116,7 +2120,7 @@ "properties": { "parameters": { "title": "Parameters for the remote invoke command", - "description": "Available parameters for the remote invoke command:\n* stack_name:\nName of the stack to get the resource information from\n* event:\nThe event that will be sent to the resource. The target parameter will depend on the resource type. For instance: 'Payload' for Lambda which can be passed as a JSON string, 'Input' for Step Functions, 'MessageBody' for SQS, and 'Data' for Kinesis data streams.\n* event_file:\nThe file that contains the event that will be sent to the resource.\n* test_event_name:\nName of the remote test event to send to the resource\n* output:\nOutput the results from the command in a given output format. The text format prints a readable AWS API response. The json format prints the full AWS API response.\n* parameter:\nAdditional parameters that can be passed to invoke the resource.\n\nLambda Function(Buffered stream): The following additional parameters can be used to invoke a lambda resource and get a buffered response: InvocationType='Event'|'RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string' Qualifier='string'.\n\nLambda Function(Response stream): The following additional parameters can be used to invoke a lambda resource with response streaming: InvocationType='RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string', Qualifier='string'.\n\nStep Functions: The following additional parameters can be used to start a state machine execution: name='string', traceHeader='string'\n\nSQS Queue: The following additional parameters can be used to send a message to an SQS queue: DelaySeconds=integer, MessageAttributes='json string', MessageSystemAttributes='json string', MessageDeduplicationId='string', MessageGroupId='string'\n\nKinesis Data Stream: The following additional parameters can be used to put a record in the kinesis data stream: PartitionKey='string', ExplicitHashKey='string', SequenceNumberForOrdering='string', StreamARN='string'\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", + "description": "Available parameters for the remote invoke command:\n* stack_name:\nName of the stack to get the resource information from\n* event:\nThe event that will be sent to the resource. The target parameter will depend on the resource type. For instance: 'Payload' for Lambda which can be passed as a JSON string, 'Input' for Step Functions, 'MessageBody' for SQS, and 'Data' for Kinesis data streams.\n* event_file:\nThe file that contains the event that will be sent to the resource.\n* test_event_name:\nName of the remote test event to send to the resource\n* output:\nOutput the results from the command in a given output format. The text format prints a readable AWS API response. The json format prints the full AWS API response.\n* parameter:\nAdditional parameters that can be passed to invoke the resource.\n\nLambda Function (Buffered stream): The following additional parameters can be used to invoke a lambda resource and get a buffered response: InvocationType='Event'|'RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string' Qualifier='string'.\n\nLambda Function (Response stream): The following additional parameters can be used to invoke a lambda resource with response streaming: InvocationType='RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string', Qualifier='string'.\n\nStep Functions: The following additional parameters can be used to start a state machine execution: name='string', traceHeader='string'\n\nSQS Queue: The following additional parameters can be used to send a message to an SQS queue: DelaySeconds=integer, MessageAttributes='json string', MessageSystemAttributes='json string', MessageDeduplicationId='string', MessageGroupId='string'\n\nKinesis Data Stream: The following additional parameters can be used to put a record in the kinesis data stream: PartitionKey='string', ExplicitHashKey='string', SequenceNumberForOrdering='string', StreamARN='string'\n* beta_features:\nEnable/Disable beta features.\n* debug:\nTurn on debug logging to print debug message generated by AWS SAM CLI and display timestamps.\n* profile:\nSelect a specific profile from your credential file to get AWS credentials.\n* region:\nSet the AWS Region of the service. (e.g. us-east-1)\n* save_params:\nSave the parameters provided via the command line to the configuration file.", "type": "object", "properties": { "stack_name": { @@ -2152,7 +2156,7 @@ "parameter": { "title": "parameter", "type": "array", - "description": "Additional parameters that can be passed to invoke the resource.\n\nLambda Function(Buffered stream): The following additional parameters can be used to invoke a lambda resource and get a buffered response: InvocationType='Event'|'RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string' Qualifier='string'.\n\nLambda Function(Response stream): The following additional parameters can be used to invoke a lambda resource with response streaming: InvocationType='RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string', Qualifier='string'.\n\nStep Functions: The following additional parameters can be used to start a state machine execution: name='string', traceHeader='string'\n\nSQS Queue: The following additional parameters can be used to send a message to an SQS queue: DelaySeconds=integer, MessageAttributes='json string', MessageSystemAttributes='json string', MessageDeduplicationId='string', MessageGroupId='string'\n\nKinesis Data Stream: The following additional parameters can be used to put a record in the kinesis data stream: PartitionKey='string', ExplicitHashKey='string', SequenceNumberForOrdering='string', StreamARN='string'", + "description": "Additional parameters that can be passed to invoke the resource.\n\nLambda Function (Buffered stream): The following additional parameters can be used to invoke a lambda resource and get a buffered response: InvocationType='Event'|'RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string' Qualifier='string'.\n\nLambda Function (Response stream): The following additional parameters can be used to invoke a lambda resource with response streaming: InvocationType='RequestResponse'|'DryRun', LogType='None'|'Tail', ClientContext='base64-encoded string', Qualifier='string'.\n\nStep Functions: The following additional parameters can be used to start a state machine execution: name='string', traceHeader='string'\n\nSQS Queue: The following additional parameters can be used to send a message to an SQS queue: DelaySeconds=integer, MessageAttributes='json string', MessageSystemAttributes='json string', MessageDeduplicationId='string', MessageGroupId='string'\n\nKinesis Data Stream: The following additional parameters can be used to put a record in the kinesis data stream: PartitionKey='string', ExplicitHashKey='string', SequenceNumberForOrdering='string', StreamARN='string'", "items": { "type": "string" } diff --git a/tests/integration/buildcmd/test_build_cmd.py b/tests/integration/buildcmd/test_build_cmd.py index 64a7c0851e..290521f289 100644 --- a/tests/integration/buildcmd/test_build_cmd.py +++ b/tests/integration/buildcmd/test_build_cmd.py @@ -627,6 +627,16 @@ def test_with_default_requirements(self, runtime, codeuri, use_container, archit runtime, codeuri, use_container, self.test_data_path, architecture=architecture ) + def test_invalid_architecture(self): + overrides = {"Runtime": "python3.11", "Architectures": "fake"} + cmdlist = self.get_command_list(parameter_overrides=overrides) + process_execute = run_command(cmdlist, cwd=self.working_dir) + + self.assertEqual(1, process_execute.process.returncode) + + self.assertIn("Build Failed", str(process_execute.stdout)) + self.assertIn("Architecture fake is not supported", str(process_execute.stderr)) + class TestBuildCommand_ErrorCases(BuildIntegBase): def test_unsupported_runtime(self): @@ -1521,6 +1531,31 @@ def test_build_layer_with_architecture_not_compatible(self, build_method, use_co # Build should still succeed self.assertEqual(command_result.process.returncode, 0) + def test_build_layer_with_makefile_with_fake_build_architecture(self): + build_method = "makefile" + use_container = False + + # Re-use the same test Layer, this time with just a bad BuildArchitecture + layer_identifier = "LayerWithNoCompatibleArchitectures" + + overrides = { + "LayerBuildMethod": build_method, + "LayerMakeContentUri": "PyLayerMake", + "LayerBuildArchitecture": "fake", + } + cmdlist = self.get_command_list( + use_container=use_container, parameter_overrides=overrides, function_identifier=layer_identifier + ) + + command_result = run_command(cmdlist, cwd=self.working_dir) + # Capture warning + self.assertIn( + "`fake` in Layer `LayerWithNoCompatibleArchitectures` is not a valid architecture", + str(command_result.stderr), + ) + # Build should still succeed + self.assertEqual(command_result.process.returncode, 0) + @parameterized.expand([("python3.7", False, "LayerTwo"), ("python3.7", "use_container", "LayerTwo")]) def test_build_fails_with_missing_metadata(self, runtime, use_container, layer_identifier): if use_container and (SKIP_DOCKER_TESTS or SKIP_DOCKER_BUILD): diff --git a/tests/unit/lib/utils/test_build_utils.py b/tests/unit/lib/utils/test_build_utils.py new file mode 100644 index 0000000000..ea00d6d5c1 --- /dev/null +++ b/tests/unit/lib/utils/test_build_utils.py @@ -0,0 +1,31 @@ +from unittest import TestCase +from samcli.lib.build.utils import valid_architecture, warn_on_invalid_architecture +from samcli.lib.utils.architecture import X86_64, ARM64 +from unittest.mock import patch, MagicMock, ANY + + +class TestBuildUtils(TestCase): + def test_valid_architecture(self): + self.assertTrue(valid_architecture(X86_64)) + self.assertTrue(valid_architecture(ARM64)) + self.assertFalse(valid_architecture("fake")) + + @patch("samcli.lib.build.utils.LOG") + def test_warn_on_invalid_architecture(self, patched_logger): + good_layer_definition = MagicMock() + good_layer_definition.architecture = X86_64 + good_layer_definition.layer = MagicMock() + good_layer_definition.layer.layer_id = "foo" + + warn_on_invalid_architecture(good_layer_definition) + patched_logger.warning.assert_not_called() + + bad_layer_definition = MagicMock() + bad_layer_definition.architecture = "fake" + bad_layer_definition.layer = MagicMock() + bad_layer_definition.layer.layer_id = "foo" + + warn_on_invalid_architecture(bad_layer_definition) + patched_logger.warning.assert_called_with( + "WARNING: `%s` in Layer `%s` is not a valid architecture.", "fake", "foo" + ) diff --git a/tests/unit/local/docker/test_lambda_build_container.py b/tests/unit/local/docker/test_lambda_build_container.py index 19f4aefe99..e260a14704 100644 --- a/tests/unit/local/docker/test_lambda_build_container.py +++ b/tests/unit/local/docker/test_lambda_build_container.py @@ -11,7 +11,7 @@ from parameterized import parameterized from samcli.lib.utils.architecture import X86_64, ARM64 -from samcli.local.docker.lambda_build_container import LambdaBuildContainer +from samcli.local.docker.lambda_build_container import LambdaBuildContainer, InvalidArchitectureForImage class TestLambdaBuildContainer_init(TestCase): @@ -223,6 +223,10 @@ class TestLambdaBuildContainer_get_image_tag(TestCase): def test_must_get_image_tag(self, architecture, expected_image_tag): self.assertEqual(expected_image_tag, LambdaBuildContainer.get_image_tag(architecture)) + def test_must_raise_an_error_for_invalid_architecture(self): + with self.assertRaises(InvalidArchitectureForImage): + LambdaBuildContainer.get_image_tag("invalid-architecture") + class TestLambdaBuildContainer_get_entrypoint(TestCase): def test_must_get_entrypoint(self):