Skip to content
This repository was archived by the owner on Aug 22, 2022. It is now read-only.

Update requirements and dependency badge #153

Merged
merged 19 commits into from
Nov 14, 2016
Merged

Conversation

itsjeyd
Copy link
Member

@itsjeyd itsjeyd commented Nov 10, 2016

cf. OC-2024

Notes

  • I could not update PyYAML, selenium, and Werkzeug to the most recent versions available, since Jasmine currently still depends on slightly older versions.

  • Do not ignore overridden View.get_view_name() in breadcrumbs encode/django-rest-framework#3273 changed the way breadcrumbs are generated for browsable API pages: Before, DRT would call a generic get_view_name function to obtain view names to use for individual breadcrumbs. That function only uses information that it can obtain from the view class, and does not depend on the action attribute being set on the view. Now, the code that generates breadcrumbs instantiates a view based on the view class and calls the (overridden) get_view_name function on it. This broke a couple of tests for the instance and app server APIs (OpenEdXInstanceAPITestCase.test_view_name and OpenEdXAppServerAPITestCase.test_view_name), because our custom implementations of get_view_name (InstanceViewSet.get_view_name and OpenEdXAppServerViewSet.get_view_name) depended on the action attribute being set. DRT will only set the .action attribute on a view in the context of a request, so when the breadcrumbs code called get_view_name on a newly instantiated InstanceViewSet or OpenEdXAppServerViewSet, that call would fail with AttributeError: 'ViewSet' object has no attribute 'action'..

    faa6214 fixes this issue by using getattr to retrieve the value of the action field, allowing the breadcrumbs code to finish successfully. However, a small problem remains: When generating breadcrumbs using the new method, DRT itself can not distinguish between list and detail views of an InstanceViewSet/OpenEdXAppServerViewSet (since suffix is None). This results in breadcrumbs being displayed as

    Api Root / Instance None / Instance None

    on the details page of an instance. On master (which uses an older version of DRT that precedes Do not ignore overridden View.get_view_name() in breadcrumbs encode/django-rest-framework#3273) we instead got:

    Api Root / Instance List / Instance Instance

    It's possible that I'm missing something, but given that we can only use the value of the action attribute to determine an appropriate view name in the context of a request, I'm not sure how we could go about determining an appropriate view name in the context of breadcrumbs generation.

    There's always the option of sticking with an older version of DRT, or using a custom fork that reverts the changes from Do not ignore overridden View.get_view_name() in breadcrumbs encode/django-rest-framework#3273; but at this point, that would mean using a significantly older version of DRT (3.3.3 vs. 3.5.3), or introducing additional overhead for maintaining a separate fork.

    Update: Custom get_view_name methods have been removed completely, cf. relevant discussion starting with Update requirements and dependency badge #153 (comment). Keeping this note for future reference.

Test instructions

  • Observe green build on this PR.
  • Run make shell locally to see that it starts up successfully.
  • Run the development server. Observe that there are no deprecation warnings in the output. Check that you can access the instance manager in the browser (shouldn't raise a DisallowedHost exception). Note that if you run management commands with PYTHONWARNINGS=all, you'll still see some warnings, but they concern code installed into the venv; they are not triggered by instance manager code.

Reviewers

@itsjeyd itsjeyd changed the title WIP: Update requirements and dependency badge Update requirements and dependency badge Nov 10, 2016
@@ -98,7 +98,8 @@ test_unit: clean static_external

# Check whether migrations need to be generated
test_migrations_missing: clean
! honcho -e .env.test run ./manage.py makemigrations --dry-run --exit
(psql -lqt | cut -d \| -f 1 | grep -qw opencraft) || (createdb --encoding utf-8 --template template0 opencraft)
Copy link
Member

Choose a reason for hiding this comment

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

If I understand correctly, this new line creates the DB if it doesn't exist, right? Why is this needed here?

Copy link
Member Author

Choose a reason for hiding this comment

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

@antoviaque Yup, that's what it does. As for why it's necessary, cf. ac9be4f: After the updates, makemigrations fails with "OperationalError: database ... does not exist" if the target database does not exist (this is actually what I would expect). I first noticed this on CircleCI, and then also verified it locally, by changing DATABASE_URL in .env.test to point to a database that didn't exist on my Vagrant box and running make test_migrations_missing. Didn't get any errors with the old dependencies/master, and saw the error mentioned above after switching back to the new dependencies/this branch.

Copy link
Contributor

Choose a reason for hiding this comment

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

@itsjeyd Any idea why this is happening now, and didn't happen in the past? It looks like this should always have been an issue. I can't figure out by what mechanism the DB was created on CircleCI before this PR.

In any case, the make target test_migrations_missing seems like a weird place for the implicit creation of the database. If this is indeed necessary, it can be simplified to

createdb --encoding utf-8 --template template0 opencraft 2> /dev/null || true

Copy link
Member Author

@itsjeyd itsjeyd Nov 10, 2016

Choose a reason for hiding this comment

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

@smarnach I don't know why the behavior changed, but I'm assuming it has something to do with the (Django) upgrade. As mentioned earlier, it's possible to reproduce the before/after behavior that CircleCI showed locally: In .env.test, set DATABASE_URL to point to a database that doesn't exist (e.g., DATABASE_URL='postgres://localhost/foo'). Then try running make test_migrations_missing

  • on a Vagrant snapshot with dependencies from master installed
  • on a Vagrant snapshot with dependencies from this branch (update-dependencies) installed

In the first instance, you won't get any errors. In the second instance, the command will error out with

django.db.utils.OperationalError: FATAL: database "foo" does not exist

This (combined with the fact that I also couldn't find any code that would create an "opencraft" database on CircleCI) makes me think that there never was an "opencraft" database on CircleCI, and that we didn't notice because make test_migrations_missing succeeded anyway.

I simplified the command according to your suggestions.

# (via `self.action`) inside this method will fail with an `AttributeError`.
# We guard against that here by using `getattr` to retrieve the value
# of the `action` field.
if getattr(self, 'action', None) == 'retrieve':
suffix = "Details"
return "Instance {}".format(suffix)
Copy link
Member

Choose a reason for hiding this comment

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

To understand this better, why do we change the view name depending on the action? Is there a broader fix/refactoring which would allow to prevent burying ourselves further here?

Copy link
Member Author

@itsjeyd itsjeyd Nov 10, 2016

Choose a reason for hiding this comment

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

@antoviaque We change the view name to get a better/more specific heading displayed in the browsable API. When overriding get_view_name, we get "Instance List" on /api/v1/instance/, and "Instance Details" on /api/v1/instance/{pk}/:

  • instance-list-custom-view-name
  • instance-details-custom-view-name

When not overriding get_view_name, we get "Instance List" on api/v1/instance/ (same as before), but on /api/v1/instance/{pk}/ we get "Instance Instance":

  • instance-list-default-view-name
  • instance-details-default-view-name

I'm not that familiar with DRT, so I'm not sure if there is a broader fix/refactoring that we could apply here (if there is, it would probably also take care of the breadcrumbs issue that I laid out in the PR description).

EDIT: Of course, assuming that we don't want to expose the browsable API to end users, we could also decide that we don't care about "Instance Details" vs. "Instance Instance", and just remove any custom get_view_name implementations.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm in favour of removing the get_view_name() methods. They don't seem to be worth the trouble.

Copy link
Member Author

Choose a reason for hiding this comment

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

@smarnach I agree -- removed those methods in a7a0712.

CC @antoviaque

Copy link
Member

Choose a reason for hiding this comment

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

+1 for the solution - thanks!

# (via `self.action`) inside this method will fail with an `AttributeError`.
# We guard against that here by using `getattr` to retrieve the value
# of the `action` field.
if getattr(self, 'action', None) == 'retrieve':
Copy link
Member

Choose a reason for hiding this comment

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

The duplication of the hacky fix & its explanation comment make me double down on my question in instance/api/instance.py

@@ -45,7 +46,7 @@
# Keep the secret key used in production secret
SECRET_KEY = env('SECRET_KEY')

ALLOWED_HOSTS = env.json('ALLOWED_HOSTS', default='[]')
ALLOWED_HOSTS = json.loads(env.json('ALLOWED_HOSTS', default='[]'))
Copy link
Member

Choose a reason for hiding this comment

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

Isn't env.json() already supposed to deserialize the variable string?

Copy link
Member Author

Choose a reason for hiding this comment

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

@antoviaque You'd think that it would, and the docs are terse; but I double-checked this in the shell and found that it does indeed return a string:

In [9]: import environ
In [11]: env = environ.Env()
In [12]: bar = env.json('BAR', default='[]')
In [13]: bar
Out[13]: '[]'
In [14]: type(bar)
Out[14]: str

Copy link
Contributor

Choose a reason for hiding this comment

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

It returns a string because the default value is a string. Please remove the quotes from the default value, and remove the call to json.loads().

@itsjeyd itsjeyd force-pushed the update-dependencies branch from 85a9426 to ac9be4f Compare November 10, 2016 12:18
@itsjeyd
Copy link
Member Author

itsjeyd commented Nov 10, 2016

Rebased from 85a9426.

@smarnach
Copy link
Contributor

@itsjeyd I've done a first pass over this PR. I'll manually test once the comments are addressed.

@itsjeyd
Copy link
Member Author

itsjeyd commented Nov 10, 2016

Thanks @smarnach! I'll address your comments later on today (have been working on fixing additional warnings that came up when running commands with PYTHONWARNINGS=all).

itsjeyd added a commit that referenced this pull request Nov 10, 2016
@itsjeyd
Copy link
Member Author

itsjeyd commented Nov 10, 2016

@smarnach Thanks for the review! I addressed your comments, please have another look.

test_migrations_missing: clean
! honcho -e .env.test run ./manage.py makemigrations --dry-run --exit
createdb --encoding utf-8 --template template0 opencraft 2> /dev/null || true
Copy link
Member

Choose a reason for hiding this comment

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

Can this be put in its own create_db rule, and the rule added as a dependency for test_migrations_missing? Or, better, rather than always get test_migrations_missing to execute it, add make create_db just for circleci in

pre:
and replace https://github.com/open-craft/opencraft/blob/master/bin/bootstrap#L52-L53 to factorize.

# (via `self.action`) inside this method will fail with an `AttributeError`.
# We guard against that here by using `getattr` to retrieve the value
# of the `action` field.
if getattr(self, 'action', None) == 'retrieve':
suffix = "Details"
return "Instance {}".format(suffix)
Copy link
Member

Choose a reason for hiding this comment

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

+1 for the solution - thanks!

Copy link
Member

@antoviaque antoviaque left a comment

Choose a reason for hiding this comment

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

Thanks for the changes! One last remark for the DB creation for CircleCI in the Makefile, but all good on my side once this is addressed.

…tion.

The PR containing the commit that we were referencing (mfogel/django-simple-email-confirmation#14) was closed, but the latest upstream version contains commits that implement Django 1.8+ compatibility.
They were failing with "TypeError: the JSON object must be str, not 'bytes'".
They were failing with "TypeError: 'Connection' object is not callable".
Fixes error keeping `make shell` from successfully starting up a shell.
`test_get_view_name` tests for `InstanceViewSet` and `OpenEdXAppServerViewSet` were failing with AttributeError: 'ViewSet' object has no attribute 'action'.
…owedHost exception.

We didn't run into this problem before when running the development server because up until Django 1.10.3, ALLOWED_HOSTS wasn't checked at all if DEBUG=True (cf. https://docs.djangoproject.com/en/1.10/ref/settings/#allowed-hosts). Now, the host is validated against ['localhost', '127.0.0.1', '[::1]'] if DEBUG=True *and* ALLOWED_HOSTS is an empty list.
Set PyYAML, selenium, Werkzeug dependencies to most recent versions required by Jasmine.
Was failing with "django.db.utils.OperationalError: FATAL:  database "opencraft" does not exist" on CircleCI. Target now creates default database if it does not exist.
From https://docs.djangoproject.com/en/1.10/releases/1.10/#features-removed-in-1-10:

"Session verification is enabled regardless of whether or not 'django.contrib.auth.middleware.SessionAuthenticationMiddleware' is in MIDDLEWARE_CLASSES. SessionAuthenticationMiddleware no longer has any purpose and can be removed from MIDDLEWARE_CLASSES. It's kept as a stub until Django 2.0 as a courtesy for users who don’t read this note."
@smarnach smarnach force-pushed the update-dependencies branch 2 times, most recently from f147b8e to 79c0ebf Compare November 14, 2016 14:35
@smarnach smarnach force-pushed the update-dependencies branch 2 times, most recently from cfa640b to 7e75b24 Compare November 14, 2016 15:03
@smarnach
Copy link
Contributor

@itsjeyd @antoviaque I've looked into the database creation issue. CircleCI should figure out what database to create from the DATABASE_URL environment variable. This seems to be still working, since the unit tests, which also use the database, are passing fine before the test_migrations_missing target fails. I'm currently looking into alternative solutions for this problem, but will go for the change Xavier suggested if I can't come up with anything cleaner.

I also noticed that we still have a TODO in the Vagrantfile to use the official Ubuntu image. I think we should address this TODO as part of this PR, and I started working on it.

@antoviaque
Copy link
Member

@smarnach As far as I can tell the official Ubuntu image is still broken - at least Ned was having issues with it last week (cf the ops channel). Imho this is outside the scope of this issue, I'd rather just make sure this doesn't spillover.

@smarnach
Copy link
Contributor

@antoviaque OK, fine with me. I've already taken a quick look, and it doesn't seem to be straight-forward to switch to the official image, so I'll just leave it as it is.

@smarnach smarnach force-pushed the update-dependencies branch 2 times, most recently from 2c8c3b0 to c37da1c Compare November 14, 2016 16:21
@smarnach smarnach force-pushed the update-dependencies branch 4 times, most recently from 579fa80 to c33a45e Compare November 14, 2016 16:52
@smarnach
Copy link
Contributor

Actually, ./manage.py makemigrations has no reason to access the database at all. All it should do is compare the generated migrations with the models in the .py files, independent of what's in the DB. This might be a bug in Django 1.10. I haven't figured out a way around it, so I will just use the approach suggested by Tim and Xavier.

@smarnach
Copy link
Contributor

I couldn't figure out why the database is working for the unit tests, but not working for testing for missing migrations. I tried several approaches, but the only thing that works is what Xavier suggested. The build is green now, and the only change I made was addressing Xavier's comment, so I'm merging this PR now.

@smarnach smarnach merged commit f94660e into master Nov 14, 2016
@bradenmacdonald bradenmacdonald deleted the update-dependencies branch November 14, 2016 23:17
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants