AWSLambda:list_functions() should only return the latest version by default (#4047)
This commit is contained in:
parent
a43c42ef21
commit
728c0c91b4
@ -964,7 +964,7 @@ class LambdaStorage(object):
|
|||||||
fn.policy = Policy(fn)
|
fn.policy = Policy(fn)
|
||||||
self._arns[fn.function_arn] = fn
|
self._arns[fn.function_arn] = fn
|
||||||
|
|
||||||
def publish_function(self, name_or_arn):
|
def publish_function(self, name_or_arn, description=""):
|
||||||
function = self.get_function_by_name_or_arn(name_or_arn)
|
function = self.get_function_by_name_or_arn(name_or_arn)
|
||||||
name = function.function_name
|
name = function.function_name
|
||||||
if name not in self._functions:
|
if name not in self._functions:
|
||||||
@ -975,6 +975,8 @@ class LambdaStorage(object):
|
|||||||
new_version = len(self._functions[name]["versions"]) + 1
|
new_version = len(self._functions[name]["versions"]) + 1
|
||||||
fn = copy.copy(self._functions[name]["latest"])
|
fn = copy.copy(self._functions[name]["latest"])
|
||||||
fn.set_version(new_version)
|
fn.set_version(new_version)
|
||||||
|
if description:
|
||||||
|
fn.description = description
|
||||||
|
|
||||||
self._functions[name]["versions"].append(fn)
|
self._functions[name]["versions"].append(fn)
|
||||||
self._arns[fn.function_arn] = fn
|
self._arns[fn.function_arn] = fn
|
||||||
@ -1028,13 +1030,26 @@ class LambdaStorage(object):
|
|||||||
result = []
|
result = []
|
||||||
|
|
||||||
for function_group in self._functions.values():
|
for function_group in self._functions.values():
|
||||||
if function_group["latest"] is not None:
|
latest = copy.deepcopy(function_group["latest"])
|
||||||
result.append(function_group["latest"])
|
latest.function_arn = "{}:$LATEST".format(latest.function_arn)
|
||||||
|
result.append(latest)
|
||||||
|
|
||||||
result.extend(function_group["versions"])
|
result.extend(function_group["versions"])
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def latest(self):
|
||||||
|
"""
|
||||||
|
Return the list of functions with version @LATEST
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
for function_group in self._functions.values():
|
||||||
|
if function_group["latest"] is not None:
|
||||||
|
result.append(function_group["latest"])
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class LayerStorage(object):
|
class LayerStorage(object):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -1091,6 +1106,9 @@ class LambdaBackend(BaseBackend):
|
|||||||
|
|
||||||
if spec.get("Publish"):
|
if spec.get("Publish"):
|
||||||
ver = self.publish_function(function_name)
|
ver = self.publish_function(function_name)
|
||||||
|
fn = copy.deepcopy(
|
||||||
|
fn
|
||||||
|
) # We don't want to change the actual version - just the return value
|
||||||
fn.version = ver.version
|
fn.version = ver.version
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
@ -1162,8 +1180,8 @@ class LambdaBackend(BaseBackend):
|
|||||||
def layers_versions_by_arn(self, layer_version_arn):
|
def layers_versions_by_arn(self, layer_version_arn):
|
||||||
return self._layers.get_layer_version_by_arn(layer_version_arn)
|
return self._layers.get_layer_version_by_arn(layer_version_arn)
|
||||||
|
|
||||||
def publish_function(self, function_name):
|
def publish_function(self, function_name, description=""):
|
||||||
return self._lambdas.publish_function(function_name)
|
return self._lambdas.publish_function(function_name, description)
|
||||||
|
|
||||||
def get_function(self, function_name_or_arn, qualifier=None):
|
def get_function(self, function_name_or_arn, qualifier=None):
|
||||||
return self._lambdas.get_function_by_name_or_arn(
|
return self._lambdas.get_function_by_name_or_arn(
|
||||||
@ -1210,8 +1228,10 @@ class LambdaBackend(BaseBackend):
|
|||||||
def delete_function(self, function_name, qualifier=None):
|
def delete_function(self, function_name, qualifier=None):
|
||||||
return self._lambdas.del_function(function_name, qualifier)
|
return self._lambdas.del_function(function_name, qualifier)
|
||||||
|
|
||||||
def list_functions(self):
|
def list_functions(self, func_version=None):
|
||||||
return self._lambdas.all()
|
if func_version == "ALL":
|
||||||
|
return self._lambdas.all()
|
||||||
|
return self._lambdas.latest()
|
||||||
|
|
||||||
def send_sqs_batch(self, function_arn, messages, queue_arn):
|
def send_sqs_batch(self, function_arn, messages, queue_arn):
|
||||||
success = True
|
success = True
|
||||||
|
@ -236,11 +236,12 @@ class LambdaResponse(BaseResponse):
|
|||||||
return 404, response_headers, "{}"
|
return 404, response_headers, "{}"
|
||||||
|
|
||||||
def _list_functions(self, request, full_url, headers):
|
def _list_functions(self, request, full_url, headers):
|
||||||
|
querystring = self.querystring
|
||||||
|
func_version = querystring.get("FunctionVersion", [None])[0]
|
||||||
result = {"Functions": []}
|
result = {"Functions": []}
|
||||||
|
|
||||||
for fn in self.lambda_backend.list_functions():
|
for fn in self.lambda_backend.list_functions(func_version):
|
||||||
json_data = fn.get_configuration()
|
json_data = fn.get_configuration()
|
||||||
json_data["Version"] = "$LATEST"
|
|
||||||
result["Functions"].append(json_data)
|
result["Functions"].append(json_data)
|
||||||
|
|
||||||
return 200, {}, json.dumps(result)
|
return 200, {}, json.dumps(result)
|
||||||
@ -298,8 +299,9 @@ class LambdaResponse(BaseResponse):
|
|||||||
|
|
||||||
def _publish_function(self, request, full_url, headers):
|
def _publish_function(self, request, full_url, headers):
|
||||||
function_name = self.path.rsplit("/", 2)[-2]
|
function_name = self.path.rsplit("/", 2)[-2]
|
||||||
|
description = self._get_param("Description")
|
||||||
|
|
||||||
fn = self.lambda_backend.publish_function(function_name)
|
fn = self.lambda_backend.publish_function(function_name, description)
|
||||||
if fn:
|
if fn:
|
||||||
config = fn.get_configuration()
|
config = fn.get_configuration()
|
||||||
return 201, {}, json.dumps(config)
|
return 201, {}, json.dumps(config)
|
||||||
|
@ -121,8 +121,40 @@ def test_lambda_regions(region):
|
|||||||
@mock_lambda
|
@mock_lambda
|
||||||
def test_list_functions():
|
def test_list_functions():
|
||||||
conn = boto3.client("lambda", _lambda_region)
|
conn = boto3.client("lambda", _lambda_region)
|
||||||
result = conn.list_functions()
|
conn.list_functions()["Functions"].should.have.length_of(0)
|
||||||
result["Functions"].should.have.length_of(0)
|
|
||||||
|
function_name = "testFunction"
|
||||||
|
|
||||||
|
conn.create_function(
|
||||||
|
FunctionName=function_name,
|
||||||
|
Runtime="python3.7",
|
||||||
|
Role=get_role_name(),
|
||||||
|
Handler="lambda_function.lambda_handler",
|
||||||
|
Code={"ZipFile": get_test_zip_file1()},
|
||||||
|
)
|
||||||
|
conn.list_functions()["Functions"].should.have.length_of(1)
|
||||||
|
conn.publish_version(FunctionName=function_name, Description="v2")
|
||||||
|
conn.list_functions()["Functions"].should.have.length_of(1)
|
||||||
|
|
||||||
|
# FunctionVersion=ALL means we should get a list of all versions
|
||||||
|
result = conn.list_functions(FunctionVersion="ALL")["Functions"]
|
||||||
|
result.should.have.length_of(2)
|
||||||
|
|
||||||
|
v1 = [f for f in result if f["Version"] == "1"][0]
|
||||||
|
v1["Description"].should.equal("v2")
|
||||||
|
v1["FunctionArn"].should.equal(
|
||||||
|
"arn:aws:lambda:{}:{}:function:{}:1".format(
|
||||||
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
latest = [f for f in result if f["Version"] == "$LATEST"][0]
|
||||||
|
latest["Description"].should.equal("")
|
||||||
|
latest["FunctionArn"].should.equal(
|
||||||
|
"arn:aws:lambda:{}:{}:function:{}:$LATEST".format(
|
||||||
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.network
|
@pytest.mark.network
|
||||||
@ -772,14 +804,14 @@ def test_publish():
|
|||||||
Publish=False,
|
Publish=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
function_list = conn.list_functions()
|
function_list = conn.list_functions(FunctionVersion="ALL")
|
||||||
function_list["Functions"].should.have.length_of(1)
|
function_list["Functions"].should.have.length_of(1)
|
||||||
latest_arn = function_list["Functions"][0]["FunctionArn"]
|
latest_arn = function_list["Functions"][0]["FunctionArn"]
|
||||||
|
|
||||||
res = conn.publish_version(FunctionName="testFunction")
|
res = conn.publish_version(FunctionName="testFunction")
|
||||||
assert res["ResponseMetadata"]["HTTPStatusCode"] == 201
|
assert res["ResponseMetadata"]["HTTPStatusCode"] == 201
|
||||||
|
|
||||||
function_list = conn.list_functions()
|
function_list = conn.list_functions(FunctionVersion="ALL")
|
||||||
function_list["Functions"].should.have.length_of(2)
|
function_list["Functions"].should.have.length_of(2)
|
||||||
|
|
||||||
# #SetComprehension ;-)
|
# #SetComprehension ;-)
|
||||||
@ -815,8 +847,9 @@ def test_list_create_list_get_delete_list():
|
|||||||
|
|
||||||
conn.list_functions()["Functions"].should.have.length_of(0)
|
conn.list_functions()["Functions"].should.have.length_of(0)
|
||||||
|
|
||||||
|
function_name = "testFunction"
|
||||||
conn.create_function(
|
conn.create_function(
|
||||||
FunctionName="testFunction",
|
FunctionName=function_name,
|
||||||
Runtime="python2.7",
|
Runtime="python2.7",
|
||||||
Role=get_role_name(),
|
Role=get_role_name(),
|
||||||
Handler="lambda_function.lambda_handler",
|
Handler="lambda_function.lambda_handler",
|
||||||
@ -837,10 +870,7 @@ def test_list_create_list_get_delete_list():
|
|||||||
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
"CodeSha256": hashlib.sha256(zip_content).hexdigest(),
|
||||||
"CodeSize": len(zip_content),
|
"CodeSize": len(zip_content),
|
||||||
"Description": "test lambda function",
|
"Description": "test lambda function",
|
||||||
"FunctionArn": "arn:aws:lambda:{}:{}:function:testFunction".format(
|
"FunctionName": function_name,
|
||||||
_lambda_region, ACCOUNT_ID
|
|
||||||
),
|
|
||||||
"FunctionName": "testFunction",
|
|
||||||
"Handler": "lambda_function.lambda_handler",
|
"Handler": "lambda_function.lambda_handler",
|
||||||
"MemorySize": 128,
|
"MemorySize": 128,
|
||||||
"Role": get_role_name(),
|
"Role": get_role_name(),
|
||||||
@ -853,16 +883,48 @@ def test_list_create_list_get_delete_list():
|
|||||||
},
|
},
|
||||||
"ResponseMetadata": {"HTTPStatusCode": 200},
|
"ResponseMetadata": {"HTTPStatusCode": 200},
|
||||||
}
|
}
|
||||||
func = conn.list_functions()["Functions"][0]
|
functions = conn.list_functions()["Functions"]
|
||||||
func.pop("LastModified")
|
functions.should.have.length_of(1)
|
||||||
func.should.equal(expected_function_result["Configuration"])
|
functions[0]["FunctionArn"].should.equal(
|
||||||
|
"arn:aws:lambda:{}:{}:function:{}".format(
|
||||||
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
functions = conn.list_functions(FunctionVersion="ALL")["Functions"]
|
||||||
|
functions.should.have.length_of(2)
|
||||||
|
|
||||||
|
latest = [f for f in functions if f["Version"] == "$LATEST"][0]
|
||||||
|
latest["FunctionArn"].should.equal(
|
||||||
|
"arn:aws:lambda:{}:{}:function:{}:$LATEST".format(
|
||||||
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
latest.pop("FunctionArn")
|
||||||
|
latest.pop("LastModified")
|
||||||
|
latest.should.equal(expected_function_result["Configuration"])
|
||||||
|
|
||||||
|
published = [f for f in functions if f["Version"] != "$LATEST"][0]
|
||||||
|
published["Version"].should.equal("1")
|
||||||
|
published["FunctionArn"].should.equal(
|
||||||
|
"arn:aws:lambda:{}:{}:function:{}:1".format(
|
||||||
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
func = conn.get_function(FunctionName=function_name)
|
||||||
|
|
||||||
|
func["Configuration"]["FunctionArn"].should.equal(
|
||||||
|
"arn:aws:lambda:{}:{}:function:{}".format(
|
||||||
|
_lambda_region, ACCOUNT_ID, function_name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
func = conn.get_function(FunctionName="testFunction")
|
|
||||||
# this is hard to match against, so remove it
|
# this is hard to match against, so remove it
|
||||||
func["ResponseMetadata"].pop("HTTPHeaders", None)
|
func["ResponseMetadata"].pop("HTTPHeaders", None)
|
||||||
# Botocore inserts retry attempts not seen in Python27
|
# Botocore inserts retry attempts not seen in Python27
|
||||||
func["ResponseMetadata"].pop("RetryAttempts", None)
|
func["ResponseMetadata"].pop("RetryAttempts", None)
|
||||||
func["Configuration"].pop("LastModified")
|
func["Configuration"].pop("LastModified")
|
||||||
|
func["Configuration"].pop("FunctionArn")
|
||||||
|
|
||||||
func.should.equal(expected_function_result)
|
func.should.equal(expected_function_result)
|
||||||
conn.delete_function(FunctionName="testFunction")
|
conn.delete_function(FunctionName="testFunction")
|
||||||
|
Loading…
Reference in New Issue
Block a user