Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migration from 2.2 -> 3.2 -> 4.2 #2413

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

JacobCoffee
Copy link

@JacobCoffee JacobCoffee commented Mar 19, 2024

What

  • Attempts to migration from the current 2.2 dependency set to 3.2. Then migrates from 4.2 after fixing any 3.2 issues.
  • After 4.2 migration, fixes any remaining stale dependencies by finding the first available, noting it in its respective *-requirements.txt, and bumping to the latest available non-breaking version to give "breathing room"
  • At this point, able to get a live webserver, we can apply migrations and get the loading webpage

Status

  • Tests passing
  • This does require a production database migration from PostgreSQL 10 -> 12. I'm not sure what that will entail for the infra team

Todo

  • Does the scope of this include cleaning up the BigAutoField warnings?
Auto-created primary key used when not defining a primary key type, by default 'django.db.models.AutoField'.

HINT: Configure the DEFAULT_AUTO_FIELD setting or the $SOMEMODEL.default_auto_field attribute to point to a subclass of AutoField, e.g. 'django.db.models.BigAutoField'.
  • these dont happen in current version so im going to say yes
  • resolved via e1ecf70
  • Verify this is an okay or not change and discuss path forward if not

  • Does the scope of this include cleaning up the naive datetime warning?
RuntimeWarning: DateTimeField $THING received a naive datetime (2013-08-24 00:00:00) while time zone support is active.

Closes

@@ -163,6 +163,7 @@
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'pages.middleware.PageFallbackMiddleware',
'django.contrib.redirects.middleware.RedirectFallbackMiddleware',
'allauth.account.middleware.AccountMiddleware',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right place?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems fine to me.

postgres:
image: postgres:10-bullseye
image: postgres:12-bullseye # required for 4.2 upgrades
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bullseye vs bookworm? would give us more runway on EOL (bullseye has until 2026, though)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer bookworm. And postgres:12 is also kind of old - I have postgres:16 on my local machine and it worked without problems.


django-allauth==0.41.0
django-allauth==0.61.1 # 0.55.0 is first version that supports Django 4.2
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bumped higher than least because of CVE (i think)

django-markupfield-helpers==0.1.1
django-honeypot==1.0.4 # 1.0.4 is first version that supports Django 4.2
django-markupfield==2.0.1
# django-markupfield-helpers==0.1.1 # this hasnt been moved out of alpha and is last published 2018, we do import it
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i cant find where we use this, but it seems we dont. anyway, it doesnt support either 3.2 or 4.2 iirc

django-pipeline==2.0.6
django-sitetree==1.17.0
django-pipeline==3.0.0 # 3.0.0 is first version that supports Django 4.2
django-sitetree==1.18.0 # >=1.17.1 is (?) first version that supports Django 4.2
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unclear changelog on when exactly 4.2 support was introduced

@JacobCoffee
Copy link
Author

JacobCoffee commented Mar 20, 2024

Somewhere along the way the email duplication validation from allauth became wonky causing

def test_user_duplicate_username_email(self):
to fail.
It seems to be my last failing test, barring i didnt do anything 'bad' by 'fixing' the failing tests post-migration 😅


edit: this was a change in allauth 0.52.0, but disabling it would be a negative since i believe it was added for increased security.

I can add

ACCOUNT_PREVENT_ENUMERATION = False
"""Added to ``django-allauth==0.52.0``. Set during the Django 2.2 -> 4.2 migration."""

into settings.py
or (preferably) add it only to test which seems safer.

def test_user_duplicate_username_email(self):
    """Test that a user can't be created with a duplicate username or email or both."""
    settings.ACCOUNT_PREVENT_ENUMERATION = False

it prevents people from bruteforcing to see which emails are registered
(read more at https://docs.allauth.org/en/latest/account/configuration.html -> search for ACCOUNT_PREVENT_ENUMERATION)

@JacobCoffee JacobCoffee marked this pull request as ready for review March 20, 2024 05:50
@JacobCoffee
Copy link
Author

JacobCoffee commented Mar 20, 2024

I think this is ready for someone to make fun of me now 🤣, all tests are passing

Copy link

@browniebroke browniebroke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid work 💪🏻 I noted a few things. FWIW, I only went though everything you changed (I haven't looked at what you may have missed). Also note that I'm not too familiar with the codebase, but I went through a few Django upgrades, I hope these coments are of use...

Comment on lines +34 to +38
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
"""The default primary key field type for Django models.

Required during the Django 2.2 -> 4.2 migration.
"""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this one, I noticed it generated a bunch of migrations. Should we set it to django.db.models.AutoField while we upgrade and change it after?

Reason I'm asking is that I noticed that the docs mention that not all cases are covered by the migration framework:

Unfortunately, the primary keys of existing auto-created through tables cannot currently be updated by the migrations framework.

This means that if you switch the value of DEFAULT_AUTO_FIELD and then generate migrations, the primary keys of the related models will be updated, as will the foreign keys from the through table, but the primary key of the auto-created through table will not be migrated.

In order to address this, you should add a RunSQL operation to your migrations to perform the required ALTER TABLE step. You can check the existing table name through sqlmigrate, dbshell, or with the field’s remote_field.through._meta.db_table property.

I don't know the codebase too much, so might not be an issue, but it feels like this change in primary key doesn't have to be part of the upgrade (which is quite big already).

docker-compose.yml Outdated Show resolved Hide resolved
events/tests/test_importer.py Outdated Show resolved Hide resolved
@@ -3,6 +3,6 @@ gunicorn==19.9.0
raven==6.10.0

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's an old one 😄 Not sure it supports Django 4.2, but maybe it's fine... Would be nice to see if we can switch to its successor, sentry-sdk.

users/tests/test_forms.py Show resolved Hide resolved
users/tests/test_forms.py Outdated Show resolved Hide resolved
users/tests/test_views.py Outdated Show resolved Hide resolved
Comment on lines +19 to +22
expected = ['<CodeSample: Copy One>']
published_qs = CodeSample.objects.published()
actual = [f'<CodeSample: {str(obj)}>' for obj in published_qs]
self.assertEqual(actual, expected)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this would simpler using assertCountEqual(), like this:

Suggested change
expected = ['<CodeSample: Copy One>']
published_qs = CodeSample.objects.published()
actual = [f'<CodeSample: {str(obj)}>' for obj in published_qs]
self.assertEqual(actual, expected)
self.assertCountEqual(CodeSample.objects.published(),[self.sample2])

I find the name of the method slightly misleading, but it would work nicely here, I think:

Test that sequence first contains the same elements as second, regardless of their order

https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertCountEqual

The only difference I see is that we would no longer cover the __str__ of the model. That could be separate test, if it's deemed important.

Copy link

@ephes ephes Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to rewrite those tests like this:

    def test_published(self):
        self.assertQuerySetEqual(CodeSample.objects.published(),
                                ['<CodeSample: Copy One>'], transform=repr)

@ephes
Copy link

ephes commented Mar 25, 2024

Cool, this really looks good! I didn't see your PR yesterday and started working a bit on the Django update by myself. I also didn't notice that makemigrations would create a lot of migrations, so I got the tests running again without adding those migrations. I honestly don't know whether they are necessary. It's probably a good idea not to fall too much behind, so I think they should be added.

Another thing I did different was trying to update all packages in base and dev-requirements.txt. And except tablib and urllib3 it seems to be possible to upgrade all dependencies.

@ephes
Copy link

ephes commented Mar 25, 2024

Somewhere along the way the email duplication validation from allauth became wonky causing

def test_user_duplicate_username_email(self):

to fail.
It seems to be my last failing test, barring i didnt do anything 'bad' by 'fixing' the failing tests post-migration 😅
edit: this was a change in allauth 0.52.0, but disabling it would be a negative since i believe it was added for increased security.

I can add

ACCOUNT_PREVENT_ENUMERATION = False
"""Added to ``django-allauth==0.52.0``. Set during the Django 2.2 -> 4.2 migration."""

into settings.py or (preferably) add it only to test which seems safer.

def test_user_duplicate_username_email(self):
    """Test that a user can't be created with a duplicate username or email or both."""
    settings.ACCOUNT_PREVENT_ENUMERATION = False

it prevents people from bruteforcing to see which emails are registered (read more at https://docs.allauth.org/en/latest/account/configuration.html -> search for ACCOUNT_PREVENT_ENUMERATION)

The test fails because allauth wont tell you whether an email already exists or not to mitigate against email enumeration. In 0.52.0 there was this setting added, but since 0.55.0 it has no longer an effect because it didn't really work from the start.

But even if no error message is shown to the user, an error message is sent via email to the original user of the email address. What I did was splitting up the test into two to make sure the error message is sent.

@cclauss
Copy link
Contributor

cclauss commented Mar 29, 2024

Solid work @JacobCoffee !!

A few things you might consider...

  1. Land a smaller PR on this repo so that 1 workflow awaiting approval disappears and GitHub Actions are run on commits.
  2. Perhaps upgrade to 3.2 and let the site run for a few weeks before making a separate upgrade to 4.2.
  3. Add Django to the title: feat: migration from Django 2.2 -> 3.2 -> 4.2.

@ambv Your review, please.

@ewdurbin
Copy link
Member

Current PG version running python.org's db: 15.3

@JacobCoffee
Copy link
Author

@ewdurbin is this looking okay so far? If so I can try to finish it up - but I wanted to make sure we wanted to move forward here before continuing anymore work.

@ewdurbin
Copy link
Member

@JacobCoffee Yes, overall this is looking very promising. I was able to get things stood up in a test environment with a clone of the production DB without any obvious errors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Upgrade to Django 3.2 LTS version
5 participants