Skip to content

Commit 3f3679e

Browse files
authored
fix: fix deploy to same namespace if organization_id incorrectly set (#55)
* fix: cant deploy to the same namespace if organization_id incorrectly set * chore: rework integration tests * fix(tests): fix bad imports after renaming
1 parent f62377f commit 3f3679e

21 files changed

+581
-656
lines changed

poetry.lock

Lines changed: 205 additions & 222 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scw_serverless/utils/credentials.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def get_scw_client(
1818
"""Attempts to load the profile. Will raise on invalid profiles."""
1919
client = Client.from_config_file_and_env(profile_name)
2020
_update_client_from_cli(client, secret_key, project_id, region)
21+
2122
return _validate_client(client)
2223

2324

@@ -47,3 +48,7 @@ def _update_client_from_cli(
4748
if not client.default_region:
4849
get_logger().info(f"No region was configured, using {DEFAULT_REGION}")
4950
client.default_region = DEFAULT_REGION
51+
52+
# Not used by the API framework
53+
# Can lead to issues if project_id does not belong to organization
54+
client.default_organization_id = None
File renamed without changes.

tests/app_fixtures/app.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import Any
2+
3+
from scw_serverless.app import Serverless
4+
5+
DESCRIPTION = "Say hello to the whole world."
6+
MESSAGE = "Hello World!"
7+
NAMESPACE_NAME = "integration-tests"
8+
9+
app = Serverless(NAMESPACE_NAME)
10+
11+
12+
@app.func(
13+
description=DESCRIPTION,
14+
privacy="public",
15+
env={"key": "value"},
16+
secret={},
17+
min_scale=0,
18+
max_scale=20,
19+
memory_limit=256,
20+
timeout="300s",
21+
)
22+
def hello_world(_event: dict[str, Any], _context: dict[str, Any]):
23+
return MESSAGE

tests/dev/app.py renamed to tests/app_fixtures/app_updated.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,21 @@
22

33
from scw_serverless.app import Serverless
44

5+
MESSAGE = "Hello galaxy!"
6+
DESCRIPTION = "Say hello to the whole galaxy."
7+
58
app = Serverless("integration-tests")
69

710

811
@app.func(
9-
description="This is a description",
12+
description=DESCRIPTION,
1013
privacy="public",
1114
env={"key": "value"},
1215
secret={},
1316
min_scale=0,
14-
max_scale=20,
17+
max_scale=10, # Differs from app.py
1518
memory_limit=256,
1619
timeout="300s",
1720
)
1821
def hello_world(_event: dict[str, Any], _context: dict[str, Any]):
19-
"""handle a request to the function
20-
Args:
21-
event (dict): request params
22-
context (dict): function call metadata
23-
"""
24-
25-
return "Hello World!"
22+
return MESSAGE
File renamed without changes.

tests/dev/multiple_functions.py renamed to tests/app_fixtures/multiple_functions.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,25 @@
22

33
from scw_serverless.app import Serverless
44

5+
MESSAGES = {
6+
"hello-world": "Hello World!",
7+
"cloud-of-choice": "The cloud of choice!",
8+
"scaleway": "Scaleway",
9+
}
10+
511
app = Serverless("integration-tests")
612

713

814
@app.func(memory_limit=256)
915
def hello_world(_event: dict[str, Any], _context: dict[str, Any]):
10-
return "Hello World!"
16+
return MESSAGES["hello-world"]
1117

1218

1319
@app.func(memory_limit=256)
1420
def cloud_of_choice(_event: dict[str, Any], _context: dict[str, Any]):
15-
return "The cloud of choice"
21+
return MESSAGES["cloud-of-choice"]
1622

1723

1824
@app.func(memory_limit=256)
1925
def scaleway(_event: dict[str, Any], _context: dict[str, Any]):
20-
return "Scaleway"
26+
return MESSAGES["scaleway"]

tests/constants.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import os
2+
from pathlib import Path
3+
4+
DEFAULT_REGION = "pl-waw"
5+
SCALEWAY_API_URL = "https://api.scaleway.com/"
6+
7+
COLD_START_TIMEOUT = 20
8+
9+
TESTS_DIR = os.path.realpath(os.path.dirname(__file__))
10+
11+
APP_FIXTURES_PATH = Path(TESTS_DIR, "app_fixtures")
12+
13+
APP_PY_PATH = APP_FIXTURES_PATH / "app.py"
14+
MULTIPLE_FUNCTIONS = APP_FIXTURES_PATH / "multiple_functions.py"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import re
2+
import shutil
3+
import tempfile
4+
from pathlib import Path
5+
from typing import Literal
6+
7+
from scaleway import Client
8+
9+
from ..utils import run_cli, trigger_function
10+
11+
FunctionUrl = str
12+
13+
14+
def run_deploy_command(
15+
client: Client, app_path: Path, backend: Literal["serverless", "api"] = "api"
16+
) -> list[FunctionUrl]:
17+
"""Run deploy command with a specific backend."""
18+
19+
app_dir = app_path.parent.resolve()
20+
21+
# Run the command inside a temporary directory
22+
with tempfile.TemporaryDirectory() as directory:
23+
shutil.copytree(src=app_dir, dst=directory, dirs_exist_ok=True)
24+
25+
ret = run_cli(client, directory, ["deploy", app_path.name, "-b", backend])
26+
27+
assert ret.returncode == 0, f"Non-null return code: {ret}"
28+
29+
output = ret.stderr if backend == "serverless" else ret.stdout
30+
output = str(output.decode("UTF-8")).strip()
31+
32+
# Parse the functions URL from the program output
33+
pattern = re.compile(
34+
r"(Function [a-z0-9-]+ (?:has been )?deployed to:? (https://.+))"
35+
)
36+
groups = re.findall(pattern, output)
37+
38+
function_urls = []
39+
for group in groups:
40+
function_urls.append(group[1])
41+
42+
# Call the actual function
43+
trigger_function(group[1])
44+
45+
return function_urls
Lines changed: 79 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,80 @@
11
# pylint: disable=unused-import,redefined-outer-name # fixture
2-
from tests.integrations.utils import serverless_test_project # noqa: F401
3-
from tests.integrations.utils import ServerlessTestProject
4-
from tests.utils import APP_PY_PATH
5-
6-
7-
def test_integration_deploy(
8-
serverless_test_project: ServerlessTestProject, # noqa: F811
9-
):
10-
serverless_test_project.deploy(APP_PY_PATH, backend="api")
11-
12-
13-
# Due to external factors these test will randomly fail.
14-
# def test_integration_deploy_multiple_functions():
15-
# deploy(test_utils.MULTIPLE_FUNCTIONS_PY_PATH)
16-
#
17-
#
18-
# def test_integration_deploy_existing_function():
19-
# project_id = deploy(test_utils.APP_PY_PATH, do_cleanup=False)
20-
# deploy(test_utils.APP_PY_PATH, do_cleanup=True, project_id=project_id)
21-
#
22-
#
23-
# def test_integration_deploy_multiple_existing_functions():
24-
# project_id = deploy(test_utils.MULTIPLE_FUNCTIONS_PY_PATH, do_cleanup=False)
25-
# deploy(
26-
# test_utils.MULTIPLE_FUNCTIONS_PY_PATH, do_cleanup=True, project_id=project_id
27-
# )
28-
#
29-
#
30-
# def test_integration_deploy_one_existing_function():
31-
# project_id = deploy(test_utils.APP_PY_PATH, do_cleanup=False)
32-
# deploy(
33-
# test_utils.MULTIPLE_FUNCTIONS_PY_PATH, do_cleanup=True, project_id=project_id
34-
# )
2+
3+
import scaleway.function.v1beta1 as sdk
4+
5+
from tests import constants
6+
from tests.app_fixtures import app, app_updated, multiple_functions
7+
from tests.integrations.deploy.deploy_wrapper import run_deploy_command
8+
from tests.integrations.project_fixture import scaleway_project # noqa
9+
from tests.integrations.utils import create_client, trigger_function
10+
11+
12+
def test_integration_deploy(scaleway_project: str): # noqa
13+
client = create_client()
14+
client.default_project_id = scaleway_project
15+
16+
url, *_ = run_deploy_command(
17+
client,
18+
app_path=constants.APP_PY_PATH,
19+
)
20+
21+
# Check message content
22+
resp = trigger_function(url)
23+
assert resp.text == app.MESSAGE
24+
25+
26+
def test_integration_deploy_existing_function(scaleway_project: str): # noqa
27+
client = create_client()
28+
client.default_project_id = scaleway_project
29+
30+
url, *_ = run_deploy_command(
31+
client,
32+
app_path=constants.APP_PY_PATH,
33+
)
34+
35+
# Check message content
36+
resp = trigger_function(url)
37+
assert resp.text == app.MESSAGE
38+
39+
# Get function_id
40+
api = sdk.FunctionV1Beta1API(client)
41+
namespace, *_ = api.list_namespaces_all(name=app.NAMESPACE_NAME)
42+
43+
# Check description
44+
function, *_ = api.list_functions_all(namespace_id=namespace.id, name="hello-world")
45+
assert function.description == app.DESCRIPTION
46+
47+
# Deploy twice in a row
48+
url, *_ = run_deploy_command(
49+
client,
50+
app_path=constants.APP_FIXTURES_PATH / "app_updated.py",
51+
)
52+
53+
# Check updated message content
54+
resp = trigger_function(url)
55+
assert resp.text == app_updated.MESSAGE
56+
57+
# Check updated description
58+
function = api.get_function(function_id=function.id)
59+
assert function.description == app_updated.DESCRIPTION
60+
61+
62+
def test_integration_deploy_multiple_functions(scaleway_project: str): # noqa
63+
client = create_client()
64+
client.default_project_id = scaleway_project
65+
66+
urls = run_deploy_command(
67+
client,
68+
app_path=constants.MULTIPLE_FUNCTIONS,
69+
)
70+
71+
for url in urls:
72+
# Get the function_name from the url
73+
function_name = None
74+
for name in multiple_functions.MESSAGES:
75+
if name in url:
76+
function_name = name
77+
assert function_name
78+
79+
resp = trigger_function(url)
80+
assert resp.text == multiple_functions.MESSAGES[function_name]
Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,59 @@
11
# pylint: disable=unused-import,redefined-outer-name # fixture
2-
from tests.integrations.utils import serverless_test_project # noqa: F401
3-
from tests.integrations.utils import ServerlessTestProject
4-
from tests.utils import APP_PY_PATH
52

3+
import scaleway.function.v1beta1 as sdk
64

7-
def test_integration_deploy_using_srvless_fw(
8-
serverless_test_project: ServerlessTestProject, # noqa: F811
5+
from tests import constants
6+
from tests.app_fixtures import app, app_updated
7+
from tests.integrations.deploy.deploy_wrapper import run_deploy_command
8+
from tests.integrations.project_fixture import scaleway_project # noqa
9+
from tests.integrations.utils import create_client, trigger_function
10+
11+
12+
def test_integration_deploy_serverless_backend(scaleway_project: str): # noqa
13+
client = create_client()
14+
client.default_project_id = scaleway_project
15+
16+
url, *_ = run_deploy_command(
17+
client, app_path=constants.APP_PY_PATH, backend="serverless"
18+
)
19+
20+
# Check message content
21+
resp = trigger_function(url)
22+
assert resp.text == app.MESSAGE
23+
24+
25+
def test_integration_deploy_existing_function_serverless_backend(
26+
scaleway_project: str, # noqa
927
):
10-
serverless_test_project.deploy(APP_PY_PATH, backend="serverless")
11-
12-
13-
# Due to external factors these test will randomly fail.
14-
# def test_integration_deploy_multiple_functions_using_srvless_fw():
15-
# deploy(test_utils.MULTIPLE_FUNCTIONS_PY_PATH, backend="serverless")
16-
#
17-
#
18-
# def test_integration_deploy_existing_function_using_srvless_fw():
19-
# project_id = deploy(test_utils.APP_PY_PATH, do_cleanup=False, backend="serverless")
20-
# deploy(
21-
# test_utils.APP_PY_PATH,
22-
# do_cleanup=True,
23-
# project_id=project_id,
24-
# backend="serverless",
25-
# )
26-
#
27-
#
28-
# def test_integration_deploy_multiple_existing_functions_using_srvless_fw():
29-
# project_id = deploy(
30-
# test_utils.MULTIPLE_FUNCTIONS_PY_PATH, do_cleanup=False, backend="serverless"
31-
# )
32-
# deploy(
33-
# test_utils.MULTIPLE_FUNCTIONS_PY_PATH,
34-
# do_cleanup=True,
35-
# project_id=project_id,
36-
# backend="serverless",
37-
# )
38-
#
39-
#
40-
# def test_integration_deploy_one_existing_function_using_srvless_fw():
41-
# project_id = deploy(test_utils.APP_PY_PATH, do_cleanup=False, backend="serverless")
42-
# deploy(
43-
# test_utils.MULTIPLE_FUNCTIONS_PY_PATH,
44-
# do_cleanup=True,
45-
# project_id=project_id,
46-
# backend="serverless",
47-
# )
28+
client = create_client()
29+
client.default_project_id = scaleway_project
30+
31+
url, *_ = run_deploy_command(
32+
client, app_path=constants.APP_PY_PATH, backend="serverless"
33+
)
34+
35+
# Check message content
36+
resp = trigger_function(url)
37+
assert resp.text == app.MESSAGE
38+
39+
# Get function_id
40+
api = sdk.FunctionV1Beta1API(client)
41+
namespace, *_ = api.list_namespaces_all(name=app.NAMESPACE_NAME)
42+
43+
# Check description
44+
function, *_ = api.list_functions_all(namespace_id=namespace.id, name="hello-world")
45+
assert function.description == app.DESCRIPTION
46+
47+
# Deploy twice in a row
48+
url, *_ = run_deploy_command(
49+
client,
50+
app_path=constants.APP_FIXTURES_PATH.joinpath("app_updated.py"),
51+
)
52+
53+
# Check updated message content
54+
resp = trigger_function(url)
55+
assert resp.text == app_updated.MESSAGE
56+
57+
# Check updated description
58+
function = api.get_function(function_id=function.id)
59+
assert function.description == app_updated.DESCRIPTION

0 commit comments

Comments
 (0)