1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-11 12:46:49 +00:00

fix(instances): two owners added at once keep a VM to one owner

With one owner per VM, the VM page and a user's page counted the VM's
owners and then added one in separate steps, so two owners added at the
same moment both found the VM without one, and it got two. The check
and the insert now run in one transaction that holds the ownership lock
(PostgreSQL's advisory lock; SQLite's IMMEDIATE transactions already
serialize them): the second waits for the first and is refused. The
audit row is written after the transaction, so a failure keeps its row.
This commit is contained in:
catborise 2026-10-31 00:21:06 +03:00
parent 5e93920d78
commit cd2dd517b7
4 changed files with 107 additions and 19 deletions

View file

@ -21,6 +21,7 @@ from django.views.decorators.cache import never_cache
from django.views.decorators.http import require_POST
from accounts.apps import admin_password_path
from computes.utils import lock_ownership
from logs.audit import audit, audit_vm, changed_fields, failed, snapshot
from accounts.forms import ApiTokenForm, EmailOTPForm, ProfileForm, UserSSHKeyForm
@ -221,9 +222,16 @@ def user_instance_create(request, user_id):
form = forms.UserInstanceForm(request.POST or None, initial={"user": user})
if form.is_valid():
instance = form.cleaned_data["instance"]
with audit_vm(request, instance, f"add owner {form.cleaned_data['user'].username}"):
form.save()
return redirect(reverse("accounts:account", args=[user.id]))
# the form's one-owner check again, with the insert in one step, as on the VM page
with audit_vm(request, instance, f"add owner {form.cleaned_data['user'].username}") as entry, \
transaction.atomic():
lock_ownership()
form.full_clean()
entry.skip = not form.is_valid()
if form.is_valid():
form.save()
if form.is_valid():
return redirect(reverse("accounts:account", args=[user.id]))
return render(
request,