pulumi-django-azure 1.0.26__tar.gz → 1.0.28__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pulumi-django-azure might be problematic. Click here for more details.
- {pulumi_django_azure-1.0.26/src/pulumi_django_azure.egg-info → pulumi_django_azure-1.0.28}/PKG-INFO +10 -34
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/README.md +2 -2
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/pyproject.toml +20 -20
- pulumi_django_azure-1.0.28/src/pulumi_django_azure/azure_helper.py +114 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/django_deployment.py +50 -36
- pulumi_django_azure-1.0.28/src/pulumi_django_azure/management/commands/purge_cache.py +15 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/management/commands/purge_cdn.py +1 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/middleware.py +31 -8
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/settings.py +16 -4
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28/src/pulumi_django_azure.egg-info}/PKG-INFO +10 -34
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure.egg-info/SOURCES.txt +1 -1
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure.egg-info/requires.txt +6 -6
- pulumi_django_azure-1.0.26/LICENSE +0 -21
- pulumi_django_azure-1.0.26/src/pulumi_django_azure/azure_helper.py +0 -67
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/setup.cfg +0 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/__init__.py +0 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/management/commands/__init__.py +0 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure.egg-info/dependency_links.txt +0 -0
- {pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure.egg-info/top_level.txt +0 -0
{pulumi_django_azure-1.0.26/src/pulumi_django_azure.egg-info → pulumi_django_azure-1.0.28}/PKG-INFO
RENAMED
|
@@ -1,52 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulumi-django-azure
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.28
|
|
4
4
|
Summary: Simply deployment of Django on Azure with Pulumi
|
|
5
5
|
Author-email: Maarten Ureel <maarten@youreal.eu>
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2023 YouReal BV
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
6
|
+
License-Expression: MIT
|
|
28
7
|
Project-URL: Homepage, https://gitlab.com/MaartenUreel/pulumi-django-azure
|
|
29
8
|
Keywords: django,pulumi,azure
|
|
30
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
31
9
|
Classifier: Programming Language :: Python
|
|
32
10
|
Classifier: Programming Language :: Python :: 3
|
|
33
11
|
Requires-Python: <3.14,>=3.11
|
|
34
12
|
Description-Content-Type: text/markdown
|
|
35
|
-
License-File: LICENSE
|
|
36
13
|
Requires-Dist: azure-identity<2.0.0,>=1.21.0
|
|
37
14
|
Requires-Dist: azure-keyvault-secrets<5.0.0,>=4.9.0
|
|
38
15
|
Requires-Dist: azure-mgmt-cdn<14.0.0,>=13.1.1
|
|
39
16
|
Requires-Dist: azure-mgmt-resource<24.0.0,>=23.3.0
|
|
40
|
-
Requires-Dist: django<6.0
|
|
41
|
-
Requires-Dist: django-azure-communication-email<2.0.0,>=1.3.
|
|
17
|
+
Requires-Dist: django<6.0,>=5.2
|
|
18
|
+
Requires-Dist: django-azure-communication-email<2.0.0,>=1.3.2
|
|
42
19
|
Requires-Dist: django-environ<0.13.0,>=0.12.0
|
|
43
20
|
Requires-Dist: django-redis<6.0.0,>=5.4.0
|
|
44
|
-
Requires-Dist: django-storages[azure]<2.0.0,>=1.14.
|
|
45
|
-
Requires-Dist: pulumi>=3.
|
|
46
|
-
Requires-Dist: pulumi-azure-native>=2.
|
|
47
|
-
Requires-Dist: pulumi-random>=4.18.
|
|
21
|
+
Requires-Dist: django-storages[azure]<2.0.0,>=1.14.6
|
|
22
|
+
Requires-Dist: pulumi>=3.165.0
|
|
23
|
+
Requires-Dist: pulumi-azure-native>=3.2.0
|
|
24
|
+
Requires-Dist: pulumi-random>=4.18.1
|
|
48
25
|
Requires-Dist: redis[hiredis]<6.0.0,>=5.2.1
|
|
49
|
-
Dynamic: license-file
|
|
50
26
|
|
|
51
27
|
# Pulumi Django Deployment
|
|
52
28
|
|
|
@@ -76,7 +52,7 @@ Your Django project should contain a folder `cicd` with these files:
|
|
|
76
52
|
sh cicd/collectstatic.sh &
|
|
77
53
|
|
|
78
54
|
python manage.py migrate
|
|
79
|
-
gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
|
|
55
|
+
gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --preload --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
|
|
80
56
|
```
|
|
81
57
|
|
|
82
58
|
Be sure to change `yourapplication` in the above.
|
|
@@ -168,7 +144,7 @@ django.add_database_administrator(
|
|
|
168
144
|
```python
|
|
169
145
|
from pulumi_django_azure.settings import * # noqa: F403
|
|
170
146
|
|
|
171
|
-
# This will provide the management command to purge the CDN
|
|
147
|
+
# This will provide the management command to purge the CDN and cache
|
|
172
148
|
INSTALLED_APPS += ["pulumi_django_azure"]
|
|
173
149
|
|
|
174
150
|
# This will provide the health check middleware that will also take care of credential rotation.
|
|
@@ -26,7 +26,7 @@ Your Django project should contain a folder `cicd` with these files:
|
|
|
26
26
|
sh cicd/collectstatic.sh &
|
|
27
27
|
|
|
28
28
|
python manage.py migrate
|
|
29
|
-
gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
|
|
29
|
+
gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --preload --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Be sure to change `yourapplication` in the above.
|
|
@@ -118,7 +118,7 @@ django.add_database_administrator(
|
|
|
118
118
|
```python
|
|
119
119
|
from pulumi_django_azure.settings import * # noqa: F403
|
|
120
120
|
|
|
121
|
-
# This will provide the management command to purge the CDN
|
|
121
|
+
# This will provide the management command to purge the CDN and cache
|
|
122
122
|
INSTALLED_APPS += ["pulumi_django_azure"]
|
|
123
123
|
|
|
124
124
|
# This will provide the health check middleware that will also take care of credential rotation.
|
|
@@ -4,13 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pulumi-django-azure"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.28"
|
|
8
8
|
description = "Simply deployment of Django on Azure with Pulumi"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Maarten Ureel", email = "maarten@youreal.eu" }]
|
|
11
|
-
license =
|
|
11
|
+
license = "MIT"
|
|
12
12
|
classifiers = [
|
|
13
|
-
"License :: OSI Approved :: MIT License",
|
|
14
13
|
"Programming Language :: Python",
|
|
15
14
|
"Programming Language :: Python :: 3",
|
|
16
15
|
]
|
|
@@ -20,15 +19,15 @@ dependencies = [
|
|
|
20
19
|
"azure-keyvault-secrets (>=4.9.0,<5.0.0)",
|
|
21
20
|
"azure-mgmt-cdn (>=13.1.1,<14.0.0)",
|
|
22
21
|
"azure-mgmt-resource (>=23.3.0,<24.0.0)",
|
|
23
|
-
"django (>=5.
|
|
24
|
-
"django-azure-communication-email (>=1.3.
|
|
22
|
+
"django (>=5.2,<6.0)",
|
|
23
|
+
"django-azure-communication-email (>=1.3.2,<2.0.0)",
|
|
25
24
|
"django-environ (>=0.12.0,<0.13.0)",
|
|
26
25
|
"django-redis (>=5.4.0,<6.0.0)",
|
|
27
|
-
"django-storages[azure] (>=1.14.
|
|
28
|
-
"pulumi (>=3.
|
|
29
|
-
"pulumi-azure-native (>=2.
|
|
30
|
-
"pulumi-random (>=4.18.
|
|
31
|
-
"redis[hiredis] (>=5.2.1,<6.0.0)"
|
|
26
|
+
"django-storages[azure] (>=1.14.6,<2.0.0)",
|
|
27
|
+
"pulumi (>=3.165.0)",
|
|
28
|
+
"pulumi-azure-native (>=3.2.0)",
|
|
29
|
+
"pulumi-random (>=4.18.1)",
|
|
30
|
+
"redis[hiredis] (>=5.2.1,<6.0.0)"
|
|
32
31
|
]
|
|
33
32
|
requires-python = ">=3.11,<3.14"
|
|
34
33
|
|
|
@@ -37,30 +36,31 @@ Homepage = "https://gitlab.com/MaartenUreel/pulumi-django-azure"
|
|
|
37
36
|
|
|
38
37
|
[tool.poetry]
|
|
39
38
|
name = "pulumi-django-azure"
|
|
40
|
-
version = "1.0.
|
|
41
|
-
description = "
|
|
39
|
+
version = "1.0.28"
|
|
40
|
+
description = "Simplify deployment of Django websites on Azure with Pulumi"
|
|
42
41
|
authors = ["Maarten Ureel <maarten@youreal.eu>"]
|
|
43
42
|
|
|
44
43
|
[tool.poetry.dependencies]
|
|
45
44
|
python = "^3.11,<3.14"
|
|
46
|
-
pulumi-azure-native = ">=2.89.1"
|
|
47
|
-
pulumi = ">=3.156.0"
|
|
48
|
-
pulumi-random = ">=4.18.0"
|
|
49
|
-
django-storages = {extras = ["azure"], version = "^1.14.5"}
|
|
50
45
|
azure-identity = "^1.21.0"
|
|
51
46
|
azure-keyvault-secrets = "^4.9.0"
|
|
52
47
|
azure-mgmt-cdn = "^13.1.1"
|
|
53
48
|
azure-mgmt-resource = "^23.3.0"
|
|
54
|
-
django = "^5.
|
|
55
|
-
django-azure-communication-email = "^1.3.
|
|
49
|
+
django = "^5.2"
|
|
50
|
+
django-azure-communication-email = "^1.3.2"
|
|
56
51
|
django-environ = "^0.12.0"
|
|
57
52
|
django-redis = "^5.4.0"
|
|
53
|
+
django-storages = {extras = ["azure"], version = "^1.14.6"}
|
|
54
|
+
pulumi = ">=3.165.0"
|
|
55
|
+
pulumi-azure-native = ">=3.2.0"
|
|
56
|
+
pulumi-random = ">=4.18.1"
|
|
58
57
|
redis = {extras = ["hiredis"], version = "^5.2.1"}
|
|
59
58
|
|
|
60
59
|
[tool.poetry.group.dev.dependencies]
|
|
61
|
-
twine = "^6.1.0"
|
|
62
60
|
build = "^1.2.2.post1"
|
|
63
|
-
|
|
61
|
+
pre-commit = "^4.2.0"
|
|
62
|
+
ruff = "^0.11.7"
|
|
63
|
+
twine = "^6.1.0"
|
|
64
64
|
|
|
65
65
|
[tool.ruff]
|
|
66
66
|
line-length = 140
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from subprocess import check_output
|
|
8
|
+
|
|
9
|
+
from azure.identity import DefaultAzureCredential
|
|
10
|
+
from azure.mgmt.resource import SubscriptionClient
|
|
11
|
+
from azure.mgmt.resource.subscriptions.models import Subscription
|
|
12
|
+
|
|
13
|
+
_redis_token_cache = None
|
|
14
|
+
_database_token_cache = None
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("pulumi_django_azure.azure_helper")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Azure credentials
|
|
20
|
+
AZURE_CREDENTIAL = DefaultAzureCredential()
|
|
21
|
+
|
|
22
|
+
# Get the local IP addresses of the machine (only when runnig on Azure)
|
|
23
|
+
if os.environ.get("IS_AZURE_ENVIRONMENT"):
|
|
24
|
+
LOCAL_IP_ADDRESSES = check_output(["hostname", "--all-ip-addresses"]).decode("utf-8").strip().split(" ")
|
|
25
|
+
else:
|
|
26
|
+
LOCAL_IP_ADDRESSES = []
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_db_password() -> str:
|
|
30
|
+
"""
|
|
31
|
+
Get a valid password for the database.
|
|
32
|
+
"""
|
|
33
|
+
global _database_token_cache
|
|
34
|
+
_database_token_cache = AZURE_CREDENTIAL.get_token("https://ossrdbms-aad.database.windows.net/.default")
|
|
35
|
+
|
|
36
|
+
logger.debug("New database token: %s", _database_token_cache)
|
|
37
|
+
|
|
38
|
+
return _database_token_cache.token
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def db_token_will_expire(treshold=300) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Check if the database token will expire in the next treshold seconds.
|
|
44
|
+
"""
|
|
45
|
+
# If the token is not cached, we consider it expired (so a new one will be fetched)
|
|
46
|
+
if _database_token_cache is None:
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
logger.debug("Database token expires on: %s", _database_token_cache.expires_on)
|
|
50
|
+
|
|
51
|
+
# If the token is cached, check if it will expire in the next treshold seconds
|
|
52
|
+
return _database_token_cache.expires_on - time.time() < treshold
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class RedisCredentials:
|
|
57
|
+
username: str
|
|
58
|
+
password: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_redis_credentials() -> RedisCredentials:
|
|
62
|
+
"""
|
|
63
|
+
Get valid credentials for the Redis cache.
|
|
64
|
+
"""
|
|
65
|
+
global _redis_token_cache
|
|
66
|
+
_redis_token_cache = AZURE_CREDENTIAL.get_token("https://redis.azure.com/.default")
|
|
67
|
+
|
|
68
|
+
t = _redis_token_cache.token
|
|
69
|
+
|
|
70
|
+
logger.debug("New Redis token: %s", _redis_token_cache)
|
|
71
|
+
|
|
72
|
+
return RedisCredentials(_extract_username_from_token(t), t)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def redis_token_will_expire(treshold=300) -> bool:
|
|
76
|
+
"""
|
|
77
|
+
Check if the Redis token will expire in the next treshold seconds.
|
|
78
|
+
"""
|
|
79
|
+
# If the token is not cached, we consider it expired (so a new one will be fetched)
|
|
80
|
+
if _redis_token_cache is None:
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
logger.debug("Redis token expires on: %s", _redis_token_cache.expires_on)
|
|
84
|
+
|
|
85
|
+
# If the token is cached, check if it will expire in the next treshold seconds
|
|
86
|
+
return _redis_token_cache.expires_on - time.time() < treshold
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_subscription() -> Subscription:
|
|
90
|
+
"""
|
|
91
|
+
Get the subscription for the current user.
|
|
92
|
+
"""
|
|
93
|
+
subscription_client = SubscriptionClient(AZURE_CREDENTIAL)
|
|
94
|
+
subscriptions = list(subscription_client.subscriptions.list())
|
|
95
|
+
return subscriptions[0]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _extract_username_from_token(token: str) -> str:
|
|
99
|
+
"""
|
|
100
|
+
Extract the username from the JSON Web Token (JWT) token.
|
|
101
|
+
"""
|
|
102
|
+
parts = token.split(".")
|
|
103
|
+
base64_str = parts[1]
|
|
104
|
+
|
|
105
|
+
if len(base64_str) % 4 == 2:
|
|
106
|
+
base64_str += "=="
|
|
107
|
+
elif len(base64_str) % 4 == 3:
|
|
108
|
+
base64_str += "="
|
|
109
|
+
|
|
110
|
+
json_bytes = base64.b64decode(base64_str)
|
|
111
|
+
json_str = json_bytes.decode("utf-8")
|
|
112
|
+
jwt = json.loads(json_str)
|
|
113
|
+
|
|
114
|
+
return jwt["oid"]
|
|
@@ -14,7 +14,7 @@ class HostDefinition:
|
|
|
14
14
|
:param identifier: An identifier for this host definition (optional).
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
def __init__(self, host: str, zone: azure.
|
|
17
|
+
def __init__(self, host: str, zone: azure.dns.Zone | None = None, identifier: str | None = None):
|
|
18
18
|
self.host = host
|
|
19
19
|
self.zone = zone
|
|
20
20
|
self._identifier = identifier
|
|
@@ -68,10 +68,11 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
68
68
|
app_service_sku: azure.web.SkuDescriptionArgs,
|
|
69
69
|
storage_account_name: str,
|
|
70
70
|
storage_allowed_origins: Sequence[str] | None = None,
|
|
71
|
+
pgsql_parameters: dict[str, str] | None = None,
|
|
71
72
|
pgadmin_access_ip: Sequence[str] | None = None,
|
|
72
|
-
pgadmin_dns_zone: azure.
|
|
73
|
+
pgadmin_dns_zone: azure.dns.Zone | None = None,
|
|
73
74
|
cache_ip_prefix: str | None = None,
|
|
74
|
-
cache_sku: azure.
|
|
75
|
+
cache_sku: azure.redis.SkuArgs | None = None,
|
|
75
76
|
cdn_host: HostDefinition | None = None,
|
|
76
77
|
opts=None,
|
|
77
78
|
):
|
|
@@ -88,6 +89,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
88
89
|
:param app_service_sku: The SKU for the app service plan.
|
|
89
90
|
:param storage_account_name: The name of the storage account. Should be unique across Azure.
|
|
90
91
|
:param storage_allowed_origins: The origins (hosts) to allow access through CORS policy. You can specify '*' to allow all.
|
|
92
|
+
:param pgsql_parameters: The parameters to set on the PostgreSQL server. (optional)
|
|
91
93
|
:param pgadmin_access_ip: The IP addresses to allow access to pgAdmin. If empty, all IP addresses are allowed.
|
|
92
94
|
:param pgadmin_dns_zone: The Azure DNS zone to a pgadmin DNS record in. (optional)
|
|
93
95
|
:param cache_ip_prefix: The IP prefix for the cache subnet. (optional)
|
|
@@ -111,7 +113,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
111
113
|
self._cdn_host = self._create_cdn(custom_host=cdn_host)
|
|
112
114
|
|
|
113
115
|
# PostgreSQL resources
|
|
114
|
-
self._create_database(sku=pgsql_sku, ip_prefix=pgsql_ip_prefix)
|
|
116
|
+
self._create_database(sku=pgsql_sku, ip_prefix=pgsql_ip_prefix, parameters=pgsql_parameters)
|
|
115
117
|
|
|
116
118
|
# Cache resources
|
|
117
119
|
if cache_ip_prefix and cache_sku:
|
|
@@ -220,14 +222,14 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
220
222
|
if custom_host:
|
|
221
223
|
if custom_host.zone:
|
|
222
224
|
# Create a DNS record for the custom host in the given zone
|
|
223
|
-
rs = azure.
|
|
225
|
+
rs = azure.dns.RecordSet(
|
|
224
226
|
f"cdn-cname-{self._name}",
|
|
225
227
|
resource_group_name=self._rg,
|
|
226
228
|
zone_name=custom_host.zone.name,
|
|
227
229
|
relative_record_set_name=custom_host.host,
|
|
228
230
|
record_type="CNAME",
|
|
229
231
|
ttl=3600,
|
|
230
|
-
target_resource=azure.
|
|
232
|
+
target_resource=azure.dns.SubResourceArgs(
|
|
231
233
|
id=self._cdn_endpoint.id,
|
|
232
234
|
),
|
|
233
235
|
)
|
|
@@ -257,7 +259,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
257
259
|
# Return the default CDN host name
|
|
258
260
|
return self._cdn_endpoint.host_name
|
|
259
261
|
|
|
260
|
-
def _create_database(self, sku: azure.dbforpostgresql.SkuArgs, ip_prefix: str):
|
|
262
|
+
def _create_database(self, sku: azure.dbforpostgresql.SkuArgs, ip_prefix: str, parameters: dict[str, str]):
|
|
261
263
|
# Create subnet for PostgreSQL
|
|
262
264
|
subnet = self._create_subnet(
|
|
263
265
|
name="pgsql",
|
|
@@ -266,7 +268,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
266
268
|
)
|
|
267
269
|
|
|
268
270
|
# Create private DNS zone
|
|
269
|
-
dns = azure.
|
|
271
|
+
dns = azure.privatedns.PrivateZone(
|
|
270
272
|
f"dns-pgsql-{self._name}",
|
|
271
273
|
resource_group_name=self._rg,
|
|
272
274
|
location="global",
|
|
@@ -275,7 +277,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
275
277
|
)
|
|
276
278
|
|
|
277
279
|
# Link the private DNS zone to the VNet in order to make resolving work
|
|
278
|
-
azure.
|
|
280
|
+
azure.privatedns.VirtualNetworkLink(
|
|
279
281
|
f"vnet-link-pgsql-{self._name}",
|
|
280
282
|
resource_group_name=self._rg,
|
|
281
283
|
location="global",
|
|
@@ -306,25 +308,37 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
306
308
|
),
|
|
307
309
|
)
|
|
308
310
|
|
|
311
|
+
# Add parameters
|
|
312
|
+
if parameters:
|
|
313
|
+
for name, value in parameters.items():
|
|
314
|
+
azure.dbforpostgresql.Configuration(
|
|
315
|
+
f"pgsql-config-{self._name}-{name}",
|
|
316
|
+
resource_group_name=self._rg,
|
|
317
|
+
server_name=self._pgsql.name,
|
|
318
|
+
source="user-override",
|
|
319
|
+
configuration_name=name,
|
|
320
|
+
value=value,
|
|
321
|
+
)
|
|
322
|
+
|
|
309
323
|
pulumi.export("pgsql_host", self._pgsql.fully_qualified_domain_name)
|
|
310
324
|
|
|
311
|
-
def _create_cache(self, sku: azure.
|
|
325
|
+
def _create_cache(self, sku: azure.redis.SkuArgs, ip_prefix: str):
|
|
312
326
|
# Create a Redis cache
|
|
313
|
-
self._cache = azure.
|
|
327
|
+
self._cache = azure.redis.Redis(
|
|
314
328
|
f"cache-{self._name}",
|
|
315
329
|
resource_group_name=self._rg,
|
|
316
330
|
sku=sku,
|
|
317
331
|
enable_non_ssl_port=False,
|
|
318
|
-
public_network_access=azure.
|
|
332
|
+
public_network_access=azure.redis.PublicNetworkAccess.DISABLED,
|
|
319
333
|
)
|
|
320
334
|
|
|
321
335
|
# Create an access policy that gives us access to the cache
|
|
322
|
-
self._cache_access_policy = azure.
|
|
336
|
+
self._cache_access_policy = azure.redis.AccessPolicy(
|
|
323
337
|
f"cache-access-policy-{self._name}",
|
|
324
338
|
resource_group_name=self._rg,
|
|
325
339
|
cache_name=self._cache.name,
|
|
326
|
-
# Same as the built in Data Contributor policy
|
|
327
|
-
permissions="+@all -@dangerous +cluster|info +cluster|nodes +cluster|slots allkeys",
|
|
340
|
+
# Same as the built in Data Contributor policy + flushdb permissions
|
|
341
|
+
permissions="+@all -@dangerous +flushdb +cluster|info +cluster|nodes +cluster|slots allkeys",
|
|
328
342
|
)
|
|
329
343
|
|
|
330
344
|
# Allocate a subnet for the cache
|
|
@@ -334,7 +348,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
334
348
|
)
|
|
335
349
|
|
|
336
350
|
# Create a private DNS zone for the cache
|
|
337
|
-
dns = azure.
|
|
351
|
+
dns = azure.privatedns.PrivateZone(
|
|
338
352
|
f"dns-cache-{self._name}",
|
|
339
353
|
resource_group_name=self._rg,
|
|
340
354
|
location="global",
|
|
@@ -342,7 +356,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
342
356
|
)
|
|
343
357
|
|
|
344
358
|
# Link the private DNS zone to the VNet in order to make resolving work
|
|
345
|
-
azure.
|
|
359
|
+
azure.privatedns.VirtualNetworkLink(
|
|
346
360
|
f"vnet-link-cache-{self._name}",
|
|
347
361
|
resource_group_name=self._rg,
|
|
348
362
|
location="global",
|
|
@@ -377,14 +391,14 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
377
391
|
)
|
|
378
392
|
|
|
379
393
|
# Create a DNS record for the cache
|
|
380
|
-
azure.
|
|
394
|
+
azure.privatedns.PrivateRecordSet(
|
|
381
395
|
f"dns-a-cache-{self._name}",
|
|
382
396
|
resource_group_name=self._rg,
|
|
383
397
|
private_zone_name=dns.name,
|
|
384
398
|
relative_record_set_name=self._cache.name,
|
|
385
399
|
record_type="A",
|
|
386
400
|
ttl=300,
|
|
387
|
-
a_records=[azure.
|
|
401
|
+
a_records=[azure.privatedns.ARecordArgs(ipv4_address=ip)],
|
|
388
402
|
)
|
|
389
403
|
|
|
390
404
|
def _create_subnet(
|
|
@@ -432,7 +446,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
432
446
|
sku=sku,
|
|
433
447
|
)
|
|
434
448
|
|
|
435
|
-
def _create_pgadmin_app(self, access_ip: Sequence[str] | None = None, dns_zone: azure.
|
|
449
|
+
def _create_pgadmin_app(self, access_ip: Sequence[str] | None = None, dns_zone: azure.dns.Zone | None = None):
|
|
436
450
|
# Determine the IP restrictions
|
|
437
451
|
ip_restrictions = []
|
|
438
452
|
default_restriction = azure.web.DefaultAction.ALLOW
|
|
@@ -490,20 +504,20 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
490
504
|
|
|
491
505
|
if dns_zone:
|
|
492
506
|
# Create a DNS record for the pgAdmin app
|
|
493
|
-
cname = azure.
|
|
507
|
+
cname = azure.dns.RecordSet(
|
|
494
508
|
f"dns-cname-pgadmin-{self._name}",
|
|
495
509
|
resource_group_name=self._rg,
|
|
496
510
|
zone_name=dns_zone.name,
|
|
497
511
|
relative_record_set_name="pgadmin",
|
|
498
512
|
record_type="CNAME",
|
|
499
513
|
ttl=3600,
|
|
500
|
-
cname_record=azure.
|
|
514
|
+
cname_record=azure.dns.CnameRecordArgs(
|
|
501
515
|
cname=app.default_host_name,
|
|
502
516
|
),
|
|
503
517
|
)
|
|
504
518
|
|
|
505
519
|
# For the certificate validation to work
|
|
506
|
-
txt_validation = azure.
|
|
520
|
+
txt_validation = azure.dns.RecordSet(
|
|
507
521
|
f"dns-txt-pgadmin-{self._name}",
|
|
508
522
|
resource_group_name=self._rg,
|
|
509
523
|
zone_name=dns_zone.name,
|
|
@@ -511,7 +525,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
511
525
|
record_type="TXT",
|
|
512
526
|
ttl=3600,
|
|
513
527
|
txt_records=[
|
|
514
|
-
azure.
|
|
528
|
+
azure.dns.TxtRecordArgs(
|
|
515
529
|
value=[app.custom_domain_verification_id],
|
|
516
530
|
)
|
|
517
531
|
],
|
|
@@ -631,11 +645,11 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
631
645
|
|
|
632
646
|
existing_binding.apply(_create_binding_with_cert)
|
|
633
647
|
|
|
634
|
-
def _create_comms_dns_records(self, suffix, host: HostDefinition, records: dict) -> list[azure.
|
|
648
|
+
def _create_comms_dns_records(self, suffix, host: HostDefinition, records: dict) -> list[azure.dns.RecordSet]:
|
|
635
649
|
created_records = []
|
|
636
650
|
|
|
637
651
|
# Domain validation and SPF record (one TXT record with multiple values)
|
|
638
|
-
r = azure.
|
|
652
|
+
r = azure.dns.RecordSet(
|
|
639
653
|
f"dns-comms-{suffix}-{host.identifier}-domain",
|
|
640
654
|
resource_group_name=self._rg,
|
|
641
655
|
zone_name=host.zone.name,
|
|
@@ -643,8 +657,8 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
643
657
|
record_type="TXT",
|
|
644
658
|
ttl=3600,
|
|
645
659
|
txt_records=[
|
|
646
|
-
azure.
|
|
647
|
-
azure.
|
|
660
|
+
azure.dns.TxtRecordArgs(value=[records["domain"]["value"]]),
|
|
661
|
+
azure.dns.TxtRecordArgs(value=[records["s_pf"]["value"]]),
|
|
648
662
|
],
|
|
649
663
|
)
|
|
650
664
|
created_records.append(r)
|
|
@@ -656,14 +670,14 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
656
670
|
else:
|
|
657
671
|
relative_record_set_name = f"{records[record]['name']}.{host.host}"
|
|
658
672
|
|
|
659
|
-
r = azure.
|
|
673
|
+
r = azure.dns.RecordSet(
|
|
660
674
|
f"dns-comms-{suffix}-{host.identifier}-{record}",
|
|
661
675
|
resource_group_name=self._rg,
|
|
662
676
|
zone_name=host.zone.name,
|
|
663
677
|
relative_record_set_name=relative_record_set_name,
|
|
664
678
|
record_type="CNAME",
|
|
665
679
|
ttl=records[record]["ttl"],
|
|
666
|
-
cname_record=azure.
|
|
680
|
+
cname_record=azure.dns.CnameRecordArgs(cname=records[record]["value"]),
|
|
667
681
|
)
|
|
668
682
|
created_records.append(r)
|
|
669
683
|
|
|
@@ -1026,7 +1040,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
1026
1040
|
if host.zone:
|
|
1027
1041
|
# Create a DNS record in the zone.
|
|
1028
1042
|
# We always use an A record instead of CNAME to avoid collisions with TXT records.
|
|
1029
|
-
a = azure.
|
|
1043
|
+
a = azure.dns.RecordSet(
|
|
1030
1044
|
f"dns-a-{name}-{self._name}-{host.identifier}",
|
|
1031
1045
|
resource_group_name=self._rg,
|
|
1032
1046
|
zone_name=host.zone.name,
|
|
@@ -1034,7 +1048,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
1034
1048
|
record_type="A",
|
|
1035
1049
|
ttl=3600,
|
|
1036
1050
|
a_records=[
|
|
1037
|
-
azure.
|
|
1051
|
+
azure.dns.ARecordArgs(
|
|
1038
1052
|
ipv4_address=virtual_ip,
|
|
1039
1053
|
)
|
|
1040
1054
|
],
|
|
@@ -1045,7 +1059,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
1045
1059
|
# For the certificate validation to work
|
|
1046
1060
|
relative_record_set_name = "asuid" if host.host == "@" else pulumi.Output.concat("asuid.", host.host)
|
|
1047
1061
|
|
|
1048
|
-
txt_validation = azure.
|
|
1062
|
+
txt_validation = azure.dns.RecordSet(
|
|
1049
1063
|
f"dns-txt-{name}-{self._name}-{host.identifier}",
|
|
1050
1064
|
resource_group_name=self._rg,
|
|
1051
1065
|
zone_name=host.zone.name,
|
|
@@ -1053,7 +1067,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
1053
1067
|
record_type="TXT",
|
|
1054
1068
|
ttl=3600,
|
|
1055
1069
|
txt_records=[
|
|
1056
|
-
azure.
|
|
1070
|
+
azure.dns.TxtRecordArgs(
|
|
1057
1071
|
value=[app.custom_domain_verification_id],
|
|
1058
1072
|
)
|
|
1059
1073
|
],
|
|
@@ -1126,7 +1140,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
1126
1140
|
# Grant the app access to the cache if needed.
|
|
1127
1141
|
# We need to check explicitly if it is not None because the db could also be 0.
|
|
1128
1142
|
if self._cache and cache_db is not None:
|
|
1129
|
-
azure.
|
|
1143
|
+
azure.redis.AccessPolicyAssignment(
|
|
1130
1144
|
f"ra-{name}-cache",
|
|
1131
1145
|
resource_group_name=self._rg,
|
|
1132
1146
|
cache_name=self._cache.name,
|
|
@@ -1171,7 +1185,7 @@ class DjangoDeployment(pulumi.ComponentResource):
|
|
|
1171
1185
|
|
|
1172
1186
|
return app
|
|
1173
1187
|
|
|
1174
|
-
def _strip_off_dns_zone_name(self, host: str, zone: azure.
|
|
1188
|
+
def _strip_off_dns_zone_name(self, host: str, zone: azure.dns.Zone) -> pulumi.Output[str]:
|
|
1175
1189
|
"""
|
|
1176
1190
|
Strip off the DNS zone name from the host name.
|
|
1177
1191
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from django.core.cache import cache
|
|
2
|
+
from django.core.management.base import BaseCommand
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Command(BaseCommand):
|
|
6
|
+
help = "Purges the entire cache."
|
|
7
|
+
|
|
8
|
+
def handle(self, *args, **options):
|
|
9
|
+
self.stdout.write("Purging cache...")
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
cache.clear()
|
|
13
|
+
self.stdout.write(self.style.SUCCESS("Successfully purged cache."))
|
|
14
|
+
except Exception as e:
|
|
15
|
+
self.stdout.write(self.style.ERROR(f"Failed to purge cache: {e}"))
|
{pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/middleware.py
RENAMED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
from django.conf import settings
|
|
4
5
|
from django.core.cache import cache
|
|
@@ -7,35 +8,57 @@ from django.db.utils import OperationalError
|
|
|
7
8
|
from django.http import HttpResponse
|
|
8
9
|
from django_redis import get_redis_connection
|
|
9
10
|
|
|
10
|
-
from .azure_helper import get_db_password, get_redis_credentials
|
|
11
|
+
from .azure_helper import db_token_will_expire, get_db_password, get_redis_credentials, redis_token_will_expire
|
|
11
12
|
|
|
12
|
-
logger = logging.getLogger(
|
|
13
|
+
logger = logging.getLogger("pulumi_django_azure.health_check")
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class HealthCheckMiddleware:
|
|
16
17
|
def __init__(self, get_response):
|
|
17
18
|
self.get_response = get_response
|
|
18
19
|
|
|
20
|
+
def _self_heal(self):
|
|
21
|
+
# Send HUP signal to gunicorn main thread,
|
|
22
|
+
# which will trigger new workers to start.
|
|
23
|
+
os.kill(os.getppid(), 1)
|
|
24
|
+
|
|
19
25
|
def __call__(self, request):
|
|
20
26
|
if request.path == settings.HEALTH_CHECK_PATH:
|
|
21
27
|
# Update the database credentials if needed
|
|
22
28
|
if settings.AZURE_DB_PASSWORD:
|
|
23
29
|
try:
|
|
24
|
-
|
|
30
|
+
if db_token_will_expire():
|
|
31
|
+
logger.debug("Database token will expire, fetching new credentials")
|
|
32
|
+
settings.DATABASES["default"]["PASSWORD"] = get_db_password()
|
|
33
|
+
else:
|
|
34
|
+
logger.debug("Database token is still valid, skipping credentials update")
|
|
25
35
|
except Exception as e:
|
|
26
36
|
logger.error("Failed to update database credentials: %s", str(e))
|
|
37
|
+
|
|
38
|
+
self._self_heal()
|
|
39
|
+
|
|
27
40
|
return HttpResponse(status=503)
|
|
28
41
|
|
|
29
42
|
# Update the Redis credentials if needed
|
|
30
43
|
if settings.AZURE_REDIS_CREDENTIALS:
|
|
31
44
|
try:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
if redis_token_will_expire():
|
|
46
|
+
logger.debug("Redis token will expire, fetching new credentials")
|
|
47
|
+
|
|
48
|
+
redis_credentials = get_redis_credentials()
|
|
49
|
+
|
|
50
|
+
# Re-authenticate the Redis connection
|
|
51
|
+
redis_connection = get_redis_connection("default")
|
|
52
|
+
redis_connection.execute_command("AUTH", redis_credentials.username, redis_credentials.password)
|
|
53
|
+
|
|
54
|
+
settings.CACHES["default"]["OPTIONS"]["PASSWORD"] = redis_credentials.password
|
|
55
|
+
else:
|
|
56
|
+
logger.debug("Redis token is still valid, skipping credentials update")
|
|
37
57
|
except Exception as e:
|
|
38
58
|
logger.error("Failed to update Redis credentials: %s", str(e))
|
|
59
|
+
|
|
60
|
+
self._self_heal()
|
|
61
|
+
|
|
39
62
|
return HttpResponse(status=503)
|
|
40
63
|
|
|
41
64
|
try:
|
{pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/settings.py
RENAMED
|
@@ -102,14 +102,21 @@ if IS_AZURE_ENVIRONMENT:
|
|
|
102
102
|
LOGGING = {
|
|
103
103
|
"version": 1,
|
|
104
104
|
"disable_existing_loggers": False,
|
|
105
|
+
"formatters": {
|
|
106
|
+
"timestamped": {
|
|
107
|
+
"format": "{asctime} {levelname} {message}",
|
|
108
|
+
"style": "{",
|
|
109
|
+
},
|
|
110
|
+
},
|
|
105
111
|
"handlers": {
|
|
106
112
|
"file": {
|
|
107
113
|
"level": "INFO",
|
|
108
|
-
"class": "logging.handlers.
|
|
114
|
+
"class": "logging.handlers.RotatingFileHandler",
|
|
109
115
|
"filename": "/home/LogFiles/django.log",
|
|
110
|
-
|
|
111
|
-
"
|
|
112
|
-
"backupCount":
|
|
116
|
+
# 50 MB
|
|
117
|
+
"maxBytes": 52428800,
|
|
118
|
+
"backupCount": 5,
|
|
119
|
+
"formatter": "timestamped",
|
|
113
120
|
},
|
|
114
121
|
},
|
|
115
122
|
"loggers": {
|
|
@@ -118,6 +125,11 @@ if IS_AZURE_ENVIRONMENT:
|
|
|
118
125
|
"level": "INFO",
|
|
119
126
|
"propagate": True,
|
|
120
127
|
},
|
|
128
|
+
"pulumi_django_azure": {
|
|
129
|
+
"handlers": ["file"],
|
|
130
|
+
"level": "INFO",
|
|
131
|
+
"propagate": True,
|
|
132
|
+
},
|
|
121
133
|
},
|
|
122
134
|
}
|
|
123
135
|
|
{pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28/src/pulumi_django_azure.egg-info}/PKG-INFO
RENAMED
|
@@ -1,52 +1,28 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulumi-django-azure
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.28
|
|
4
4
|
Summary: Simply deployment of Django on Azure with Pulumi
|
|
5
5
|
Author-email: Maarten Ureel <maarten@youreal.eu>
|
|
6
|
-
License: MIT
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2023 YouReal BV
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
6
|
+
License-Expression: MIT
|
|
28
7
|
Project-URL: Homepage, https://gitlab.com/MaartenUreel/pulumi-django-azure
|
|
29
8
|
Keywords: django,pulumi,azure
|
|
30
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
31
9
|
Classifier: Programming Language :: Python
|
|
32
10
|
Classifier: Programming Language :: Python :: 3
|
|
33
11
|
Requires-Python: <3.14,>=3.11
|
|
34
12
|
Description-Content-Type: text/markdown
|
|
35
|
-
License-File: LICENSE
|
|
36
13
|
Requires-Dist: azure-identity<2.0.0,>=1.21.0
|
|
37
14
|
Requires-Dist: azure-keyvault-secrets<5.0.0,>=4.9.0
|
|
38
15
|
Requires-Dist: azure-mgmt-cdn<14.0.0,>=13.1.1
|
|
39
16
|
Requires-Dist: azure-mgmt-resource<24.0.0,>=23.3.0
|
|
40
|
-
Requires-Dist: django<6.0
|
|
41
|
-
Requires-Dist: django-azure-communication-email<2.0.0,>=1.3.
|
|
17
|
+
Requires-Dist: django<6.0,>=5.2
|
|
18
|
+
Requires-Dist: django-azure-communication-email<2.0.0,>=1.3.2
|
|
42
19
|
Requires-Dist: django-environ<0.13.0,>=0.12.0
|
|
43
20
|
Requires-Dist: django-redis<6.0.0,>=5.4.0
|
|
44
|
-
Requires-Dist: django-storages[azure]<2.0.0,>=1.14.
|
|
45
|
-
Requires-Dist: pulumi>=3.
|
|
46
|
-
Requires-Dist: pulumi-azure-native>=2.
|
|
47
|
-
Requires-Dist: pulumi-random>=4.18.
|
|
21
|
+
Requires-Dist: django-storages[azure]<2.0.0,>=1.14.6
|
|
22
|
+
Requires-Dist: pulumi>=3.165.0
|
|
23
|
+
Requires-Dist: pulumi-azure-native>=3.2.0
|
|
24
|
+
Requires-Dist: pulumi-random>=4.18.1
|
|
48
25
|
Requires-Dist: redis[hiredis]<6.0.0,>=5.2.1
|
|
49
|
-
Dynamic: license-file
|
|
50
26
|
|
|
51
27
|
# Pulumi Django Deployment
|
|
52
28
|
|
|
@@ -76,7 +52,7 @@ Your Django project should contain a folder `cicd` with these files:
|
|
|
76
52
|
sh cicd/collectstatic.sh &
|
|
77
53
|
|
|
78
54
|
python manage.py migrate
|
|
79
|
-
gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
|
|
55
|
+
gunicorn --timeout 600 --workers $((($NUM_CORES*2)+1)) --preload --chdir $APP_PATH yourapplication.wsgi --access-logfile '-' --error-logfile '-'
|
|
80
56
|
```
|
|
81
57
|
|
|
82
58
|
Be sure to change `yourapplication` in the above.
|
|
@@ -168,7 +144,7 @@ django.add_database_administrator(
|
|
|
168
144
|
```python
|
|
169
145
|
from pulumi_django_azure.settings import * # noqa: F403
|
|
170
146
|
|
|
171
|
-
# This will provide the management command to purge the CDN
|
|
147
|
+
# This will provide the management command to purge the CDN and cache
|
|
172
148
|
INSTALLED_APPS += ["pulumi_django_azure"]
|
|
173
149
|
|
|
174
150
|
# This will provide the health check middleware that will also take care of credential rotation.
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
LICENSE
|
|
2
1
|
README.md
|
|
3
2
|
pyproject.toml
|
|
4
3
|
setup.cfg
|
|
@@ -13,4 +12,5 @@ src/pulumi_django_azure.egg-info/dependency_links.txt
|
|
|
13
12
|
src/pulumi_django_azure.egg-info/requires.txt
|
|
14
13
|
src/pulumi_django_azure.egg-info/top_level.txt
|
|
15
14
|
src/pulumi_django_azure/management/commands/__init__.py
|
|
15
|
+
src/pulumi_django_azure/management/commands/purge_cache.py
|
|
16
16
|
src/pulumi_django_azure/management/commands/purge_cdn.py
|
|
@@ -2,12 +2,12 @@ azure-identity<2.0.0,>=1.21.0
|
|
|
2
2
|
azure-keyvault-secrets<5.0.0,>=4.9.0
|
|
3
3
|
azure-mgmt-cdn<14.0.0,>=13.1.1
|
|
4
4
|
azure-mgmt-resource<24.0.0,>=23.3.0
|
|
5
|
-
django<6.0
|
|
6
|
-
django-azure-communication-email<2.0.0,>=1.3.
|
|
5
|
+
django<6.0,>=5.2
|
|
6
|
+
django-azure-communication-email<2.0.0,>=1.3.2
|
|
7
7
|
django-environ<0.13.0,>=0.12.0
|
|
8
8
|
django-redis<6.0.0,>=5.4.0
|
|
9
|
-
django-storages[azure]<2.0.0,>=1.14.
|
|
10
|
-
pulumi>=3.
|
|
11
|
-
pulumi-azure-native>=2.
|
|
12
|
-
pulumi-random>=4.18.
|
|
9
|
+
django-storages[azure]<2.0.0,>=1.14.6
|
|
10
|
+
pulumi>=3.165.0
|
|
11
|
+
pulumi-azure-native>=3.2.0
|
|
12
|
+
pulumi-random>=4.18.1
|
|
13
13
|
redis[hiredis]<6.0.0,>=5.2.1
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 YouReal BV
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
import json
|
|
3
|
-
import os
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from subprocess import check_output
|
|
6
|
-
|
|
7
|
-
from azure.identity import DefaultAzureCredential
|
|
8
|
-
from azure.mgmt.resource import SubscriptionClient
|
|
9
|
-
from azure.mgmt.resource.subscriptions.models import Subscription
|
|
10
|
-
|
|
11
|
-
# Azure credentials
|
|
12
|
-
AZURE_CREDENTIAL = DefaultAzureCredential()
|
|
13
|
-
|
|
14
|
-
# Get the local IP addresses of the machine (only when runnig on Azure)
|
|
15
|
-
if os.environ.get("ORYX_ENV_NAME"):
|
|
16
|
-
LOCAL_IP_ADDRESSES = check_output(["hostname", "--all-ip-addresses"]).decode("utf-8").strip().split(" ")
|
|
17
|
-
else:
|
|
18
|
-
LOCAL_IP_ADDRESSES = []
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def get_db_password() -> str:
|
|
22
|
-
"""
|
|
23
|
-
Get a valid password for the database.
|
|
24
|
-
"""
|
|
25
|
-
return AZURE_CREDENTIAL.get_token("https://ossrdbms-aad.database.windows.net/.default").token
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@dataclass
|
|
29
|
-
class RedisCredentials:
|
|
30
|
-
username: str
|
|
31
|
-
password: str
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def get_redis_credentials() -> RedisCredentials:
|
|
35
|
-
"""
|
|
36
|
-
Get valid credentials for the Redis cache.
|
|
37
|
-
"""
|
|
38
|
-
token = AZURE_CREDENTIAL.get_token("https://redis.azure.com/.default").token
|
|
39
|
-
return RedisCredentials(_extract_username_from_token(token), token)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def get_subscription() -> Subscription:
|
|
43
|
-
"""
|
|
44
|
-
Get the subscription for the current user.
|
|
45
|
-
"""
|
|
46
|
-
subscription_client = SubscriptionClient(AZURE_CREDENTIAL)
|
|
47
|
-
subscriptions = list(subscription_client.subscriptions.list())
|
|
48
|
-
return subscriptions[0]
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _extract_username_from_token(token: str) -> str:
|
|
52
|
-
"""
|
|
53
|
-
Extract the username from the JSON Web Token (JWT) token.
|
|
54
|
-
"""
|
|
55
|
-
parts = token.split(".")
|
|
56
|
-
base64_str = parts[1]
|
|
57
|
-
|
|
58
|
-
if len(base64_str) % 4 == 2:
|
|
59
|
-
base64_str += "=="
|
|
60
|
-
elif len(base64_str) % 4 == 3:
|
|
61
|
-
base64_str += "="
|
|
62
|
-
|
|
63
|
-
json_bytes = base64.b64decode(base64_str)
|
|
64
|
-
json_str = json_bytes.decode("utf-8")
|
|
65
|
-
jwt = json.loads(json_str)
|
|
66
|
-
|
|
67
|
-
return jwt["oid"]
|
|
File without changes
|
{pulumi_django_azure-1.0.26 → pulumi_django_azure-1.0.28}/src/pulumi_django_azure/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|