Skip to content

Commit 9883a6e

Browse files
authored
Merge pull request #50 from octue/updates-pre-unfold
Updates pre unfold
2 parents 8ee28bf + cf2d9a8 commit 9883a6e

File tree

7 files changed

+164
-27
lines changed

7 files changed

+164
-27
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "django-gcp"
3-
version = "0.10.5"
3+
version = "0.10.6"
44
description = "Utilities to run Django on Google Cloud Platform"
55
authors = ["Tom Clark"]
66
license = "MIT"

terraform/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ It's already proven invaluable for testing and development of the tasks module.
88
We're currently learning terraform and expanding our DevOps expertise, so expect our workflows
99
to change dramatically in this area.
1010

11-
In the meaantime, used with a different project ID, performing a `terraform apply`
11+
In the meantime, used with a different project ID, performing a `terraform apply`
1212
on a fresh GCP project with this configuration (supply the project ID as a variable) should
1313
give you the resources required to run the management tasks to demonstrate the example test
1414
server (you'll need to manage service accounts yourself at the time of writing).

terraform/iam.tf

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,51 @@
11
# You need to start with a service account called "terraform" which has both the 'editor' and 'owner' basic permissions.
22
# This allows it to assign permissions to resources per https://cloud.google.com/iam/docs/understanding-roles
3-
#
4-
# Start by assigning the permissions that it needs itself
5-
6-
# Allows django-gcp.tasks to create periodic tasks for you using google cloud scheduler
7-
# resource "google_project_iam_binding" "terraform_serviceaccount_bindings" {
8-
# count = length(var.terraform_serviceaccount_roles)
9-
# project = var.project
10-
# role = var.terraform_serviceaccount_roles[count.index]
11-
# members = [
12-
# "serviceAccount:[email protected]",
13-
# ]
14-
# }
3+
154

165
resource "google_service_account" "dev_thclark" {
176
account_id = "dev-thclark"
187
display_name = "dev-thclark"
19-
project = "octue-django-gcp"
8+
project = var.project
9+
}
10+
11+
12+
resource "google_service_account" "dev_lukasvinclav" {
13+
account_id = "dev-lukasvinclav"
14+
display_name = "dev-lukasvinclav"
15+
project = var.project
16+
}
17+
18+
19+
# For iam bindings to storage buckets see terraform/storage.tf
20+
21+
22+
resource "google_project_iam_binding" "errorreporting_writer" {
23+
project = var.project
24+
role = "roles/errorreporting.writer"
25+
members = [
26+
"serviceAccount:${google_service_account.dev_thclark.email}",
27+
"serviceAccount:${google_service_account.dev_lukasvinclav.email}",
28+
]
2029
}
2130

2231

23-
# Allows django-gcp.tasks to create periodic tasks for you using google cloud scheduler
24-
# resource "google_project_iam_binding" "cloudscheduler_jobs_update" {
25-
# project = var.project
26-
# role = "roles/CloudSchedulerAdmin"
32+
# Allow django-gcp.tasks to create and update task queues
33+
resource "google_project_iam_binding" "cloudtasks_admin" {
34+
project = var.project
35+
role = "roles/cloudtasks.admin"
36+
members = [
37+
"serviceAccount:${google_service_account.dev_thclark.email}",
38+
"serviceAccount:${google_service_account.dev_lukasvinclav.email}",
39+
]
40+
}
41+
2742

28-
# members = [
29-
# "serviceAccount:${google_service_account.dev_thclark.email}",
30-
# ]
31-
# }
43+
# Allow django-gcp.tasks to create periodic tasks in google cloud scheduler
44+
resource "google_project_iam_binding" "cloudscheduler_admin" {
45+
project = var.project
46+
role = "roles/cloudscheduler.admin"
47+
members = [
48+
"serviceAccount:${google_service_account.dev_thclark.email}",
49+
"serviceAccount:${google_service_account.dev_lukasvinclav.email}",
50+
]
51+
}

terraform/storage.tf

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,18 @@ resource "google_storage_bucket_iam_binding" "static_assets_object_viewer" {
2424
]
2525
}
2626

27+
28+
# Allow developers to run collectstatic
29+
resource "google_storage_bucket_iam_binding" "static_assets_object_admin" {
30+
bucket = google_storage_bucket.static_assets.name
31+
role = "roles/storage.objectAdmin"
32+
members = [
33+
"serviceAccount:${google_service_account.dev_thclark.email}",
34+
"serviceAccount:${google_service_account.dev_lukasvinclav.email}"
35+
]
36+
}
37+
38+
2739
# Add a media bucket (private contents)
2840
# Note: CORS are set to allow direct uploads, enabling upload of files
2941
# larger than 32 mb (Cloud Run has a hard limit on file upload size)
@@ -47,7 +59,8 @@ resource "google_storage_bucket_iam_binding" "media_assets_object_admin" {
4759
bucket = google_storage_bucket.media_assets.name
4860
role = "roles/storage.objectAdmin"
4961
members = [
50-
"serviceAccount:${google_service_account.dev_thclark.email}"
62+
"serviceAccount:${google_service_account.dev_thclark.email}",
63+
"serviceAccount:${google_service_account.dev_lukasvinclav.email}"
5164
]
5265
}
5366

@@ -78,6 +91,7 @@ resource "google_storage_bucket_iam_binding" "extra_versioned_assets_object_admi
7891
bucket = google_storage_bucket.extra_versioned_assets.name
7992
role = "roles/storage.objectAdmin"
8093
members = [
81-
"serviceAccount:${google_service_account.dev_thclark.email}"
94+
"serviceAccount:${google_service_account.dev_thclark.email}",
95+
"serviceAccount:${google_service_account.dev_lukasvinclav.email}"
8296
]
8397
}

tests/server/example/admin.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from tests.server.example.models import (
44
ExampleBlankBlobFieldModel,
55
ExampleBlobFieldModel,
6+
ExampleMultipleBlobFieldModel,
67
ExampleStorageModel,
78
ExampleUneditableBlobFieldModel,
89
)
@@ -24,7 +25,12 @@ class ExampleUneditableBlobFieldModelAdmin(admin.ModelAdmin):
2425
"""A basic admin panel to demonstrate the direct upload storage behaviour where blank=True"""
2526

2627

28+
class ExampleMultipleBlobFieldModelAdmin(admin.ModelAdmin):
29+
"""A basic admin panel to demonstrate the direct upload storage behaviour where multiple blobfields are used"""
30+
31+
2732
admin.site.register(ExampleStorageModel, ExampleStorageModelAdmin)
2833
admin.site.register(ExampleBlobFieldModel, ExampleBlobFieldModelAdmin)
2934
admin.site.register(ExampleBlankBlobFieldModel, ExampleBlankBlobFieldModelAdmin)
3035
admin.site.register(ExampleUneditableBlobFieldModel, ExampleUneditableBlobFieldModelAdmin)
36+
admin.site.register(ExampleMultipleBlobFieldModel, ExampleMultipleBlobFieldModelAdmin)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Generated by Django 4.1.7 on 2023-11-17 15:19
2+
3+
import django_gcp.storage.fields
4+
from django.db import migrations, models
5+
6+
import tests.server.example.models
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
("example", "0005_examplecallbackblobfieldmodel_and_more"),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name="ExampleMultipleBlobFieldModel",
18+
fields=[
19+
(
20+
"id",
21+
models.AutoField(
22+
auto_created=True,
23+
primary_key=True,
24+
serialize=False,
25+
verbose_name="ID",
26+
),
27+
),
28+
("category", models.CharField(blank=True, max_length=20, null=True)),
29+
(
30+
"blob1",
31+
django_gcp.storage.fields.BlobField(
32+
accept_mimetype="*/*",
33+
default=None,
34+
get_destination_path=tests.server.example.models.get_destination_path,
35+
help_text="GCP cloud storage object",
36+
ingress_to="_tmp/",
37+
on_change=None,
38+
overwrite_mode="never",
39+
store_key="media",
40+
),
41+
),
42+
(
43+
"blob2",
44+
django_gcp.storage.fields.BlobField(
45+
accept_mimetype="*/*",
46+
default=None,
47+
get_destination_path=tests.server.example.models.get_destination_path,
48+
help_text="GCP cloud storage object",
49+
ingress_to="_tmp/",
50+
on_change=None,
51+
overwrite_mode="never",
52+
store_key="media",
53+
),
54+
),
55+
(
56+
"blob3",
57+
django_gcp.storage.fields.BlobField(
58+
accept_mimetype="*/*",
59+
default=None,
60+
get_destination_path=tests.server.example.models.get_destination_path,
61+
help_text="GCP cloud storage object",
62+
ingress_to="_tmp/",
63+
on_change=None,
64+
overwrite_mode="never",
65+
store_key="media",
66+
),
67+
),
68+
],
69+
),
70+
]

tests/server/example/models.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from uuid import uuid4
12
from django.db.models import CharField, FileField, Model
23
from django_gcp.storage.fields import BlobField
34

@@ -55,9 +56,14 @@ def get_destination_path(
5556
"""
5657
# Demonstrate using another field to name the object
5758
category = f"{instance.category}/" if instance.category is not None else ""
59+
5860
# You may wish to add a timestamp, or random string to prevent collisions
59-
# In this case we do the very simple thing of using the original name
60-
return f"{category}{original_name}", allow_overwrite
61+
# In this case we do the very simple thing of using the original name with random prefix
62+
# If you attempt to overwrite while allow_ovewrite is false, a server error will raise.
63+
# Only set allow_overwrite = True if you really, REALLY, know what you're doing!
64+
random_prefix = str(uuid4())[0:8]
65+
66+
return f"{category}{random_prefix}-{original_name}", allow_overwrite
6167

6268

6369
class ExampleStorageModel(Model):
@@ -137,6 +143,27 @@ class Meta:
137143
app_label = "example"
138144

139145

146+
class ExampleMultipleBlobFieldModel(Model):
147+
"""
148+
An example model containing multiple BlobFields
149+
This model was started as a FileField model then migrated, so check the migrations
150+
to see how that worked.
151+
"""
152+
153+
# This charfield is added to demonstrate that other fields of the model can be used
154+
# in order to set the blob name
155+
category = CharField(max_length=20, blank=True, null=True)
156+
157+
blob1 = BlobField(get_destination_path=get_destination_path, store_key="media")
158+
blob2 = BlobField(get_destination_path=get_destination_path, store_key="media")
159+
blob3 = BlobField(get_destination_path=get_destination_path, store_key="media")
160+
161+
class Meta:
162+
"""Metaclass defining this model to reside in the example app"""
163+
164+
app_label = "example"
165+
166+
140167
def my_on_change_callback(value, instance):
141168
"""Demonstrate the on_change callback functionality"""
142169
if value is None:

0 commit comments

Comments
 (0)