@@ -11,17 +11,19 @@ trying to move from working state to working state,
11
11
and using the FTs to detect any regressions.
12
12
13
13
14
- .🚧 Warning, this chapter is heavily under construction
14
+ .🚧 Warning, chapter under construction 🚧
15
15
*******************************************************************************
16
16
As part of my work on the third edition of the book,
17
- I'm rewriting the deployment chapters,
18
- but this chapter is far from ready I'm afraid.
19
- Sorry!
17
+ I'm making big changes to the deployment chapters,
18
+ and this is a very new draft.
20
19
21
- Following along with this chapter is going to be pretty
22
- much impossible while I'm still half-done .
20
+ This chapter still has a couple of TODOS in,
21
+ but its content is mostly there, so you should be able to follow along .
23
22
24
- It might be best to skip ahead to <<chapter_12_organising_test_files>>
23
+ But as always I really, really need fedback.
24
+ So please hit me up at [email protected] , or via
25
+ https://github.com/hjwp/Book-TDD-Web-Dev-Python/issues[GitHub Issues]
26
+ and Pull Requests.
25
27
26
28
*******************************************************************************
27
29
@@ -72,10 +74,13 @@ CMD ["gunicorn", "--bind", ":8888", "superlists.wsgi:application"]
72
74
73
75
74
76
As in the previous chapter, we can use the `docker build && docker run`
75
- in a single line to try out our changes by rebuilding and rerunning our container:
77
+ pattern to try out our changes by rebuilding and rerunning our container:
76
78
77
79
----
78
- docker build -t superlists . && docker run -p 8888:8888 -v ./src/db.sqlite3:/src/db.sqlite3 -it superlists
80
+ $ *docker build -t superlists . && docker run \
81
+ -p 8888:8888 \
82
+ -v ./src/db.sqlite3:/src/db.sqlite3 \
83
+ -it superlists*
79
84
----
80
85
81
86
==== The FTs catch a problem with static files
@@ -183,18 +188,18 @@ _settings.py_ that we want to change for production:
183
188
(_localhost_ for now, but someday soon, a real domain).
184
189
////
185
190
186
- * + DEBUG+ mode is all very well for hacking about on your own server,
191
+ * ` DEBUG` mode is all very well for hacking about on your own server,
187
192
but it https://docs.djangoproject.com/en/1.11/ref/settings/#debug[isn't secure].
188
193
For example, exposing raw tracebacks to the world is a bad idea.
189
194
190
195
* `SECRET_KEY` is used by Django for some of its crypto--things
191
- like cookies and CSRF protection.
192
- It's good practice to make sure the secret key in production is different
193
- from the one in your source code repo,
194
- because that code might be visible to strangers.
195
- We'll want to generate a new, random one
196
- but then keep it the same for the foreseeable future
197
- (find out more in the https://docs.djangoproject.com/en/4.2/topics/signing/[Django docs]).
196
+ like cookies and CSRF protection.
197
+ It's good practice to make sure the secret key in production is different
198
+ from the one in your source code repo,
199
+ because that code might be visible to strangers.
200
+ We'll want to generate a new, random one
201
+ but then keep it the same for the foreseeable future
202
+ (find out more in the https://docs.djangoproject.com/en/4.2/topics/signing/[Django docs]).
198
203
199
204
Development, staging and production sites always have some differences
200
205
in their configuration.
@@ -244,13 +249,16 @@ else:
244
249
are useful for Dev.
245
250
246
251
The end result is that you don't need to set any env vars for dev,
247
- but production needs both , and it will error if any are missing.
252
+ but production needs both to be set explicitly,
253
+ and it will error if any are missing.
248
254
I think this gives us a little bit of protection
249
255
against accidentally forgetting to set one.
250
256
251
257
TIP: Better to fail hard than allow a typo in an environment variable name to
252
258
leave you running with insecure settings.
253
259
260
+ ==== Setting environment variables inside the Dockerfile
261
+
254
262
Now let's set that environment variable in our Dockerfile using then `ENV` directive:
255
263
256
264
[role="sourcecode"]
@@ -265,31 +273,43 @@ CMD ["gunicorn", "--bind", ":8888", "superlists.wsgi:application"]
265
273
----
266
274
====
267
275
276
+ And try it out...
268
277
269
- ==== Setting environment variables at the docker command-line
270
-
271
- Ooops, and I forgot to set that secret key env var,
272
- mere moments after having dreamt it up.
273
278
274
279
275
280
----
281
+ $ *docker build -t superlists . && docker run \
282
+ -p 8888:8888 \
283
+ -v ./src/db.sqlite3:/src/db.sqlite3 \
284
+ -it superlists*
285
+
286
+ [...]
276
287
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
277
288
~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
289
+ # TODO: show more of traceback
278
290
----
279
291
292
+ Ooops, and I forgot to set said secret key env var,
293
+ mere seconds after having dreamt it up!
294
+
295
+
296
+ ==== Setting environment variables at the docker command-line
297
+
298
+ We've said we can't keep the secret key in our source code,
299
+ so the Dockerfile isn't an option; where else can we put it?
280
300
281
- Now, we've said we can't keep the secret key in our source code,
282
- where else can we put it?
283
301
For now, we can set it at the command line using the `-e` flag for `docker run`:
284
302
285
303
[subs="specialcharacters,quotes"]
286
304
----
287
- $ *docker build -t superlists . && \
288
- docker run -p 8888:8888 -v ./src/db.sqlite3:/src/db.sqlite3 -e DJANGO_SECRET_KEY=sekrit -it superlists*
305
+ $ *docker build -t superlists . && docker run \
306
+ -p 8888:8888 \
307
+ -v ./src/db.sqlite3:/src/db.sqlite3 \
308
+ -e DJANGO_SECRET_KEY=sekrit \
309
+ -it superlists*
289
310
----
290
311
291
-
292
- And use a test run to reassure ourselves that things still work...
312
+ With that running, we can use our FT again to see if we're back to a working state.
293
313
294
314
[role="small-code"]
295
315
[subs="specialcharacters,macros"]
@@ -300,24 +320,33 @@ AssertionError: 'To-Do' not found in 'Bad Request (400)'
300
320
----
301
321
302
322
323
+
303
324
==== ALLOWED_HOSTS is Required When Debug Mode is Turned Off
304
325
305
- Oops. Let's take a look manually: <<django-400-error>>.
326
+ Not quite! Let's take a look manually: <<django-400-error>>.
306
327
307
328
[[django-400-error]]
308
329
.An ugly 400 error
309
330
image::images/twp2_1002.png["An unfriendly page showing 400 Bad Request"]
310
331
311
- Something's gone wrong. But once again, by running our FTs frequently,
312
- we're able to identify the problem early, before we've changed too many things.
313
- In this case the only thing we've changed is _settings.py_. We've changed three
314
- settings—which one might be at fault?
332
+ We've set our two environment variables but doing so seems to have broken things.
333
+ But once again, by running our FTs frequently,
334
+ we're able to identify the problem early,
335
+ before we've changed too many things at the same time.
336
+ We've only changed two settings—which one might be at fault?
337
+
338
+ Let's use the "Googling the error message" technique again,
339
+ with the search terms "django debug false" and "400 bad request".
315
340
316
- Let's use the "Googling the error message" technique again.
341
+ Well, the very first link in my https://duckduckgo.com/?q=django+400+bad+request[search results]
342
+ was Stackoverflow suggesting that a 400 error is usually to do with `ALLOWED_HOSTS`,
343
+ and the second was the official Django docs,
344
+ which takes a bit more scrolling, but confirms it
345
+ (see <<search-results-400-bad-request>>).
317
346
318
- The very first link in my search results for
319
- https://duckduckgo.com/?q= django+400+bad+request[Django 400 Bad Request]
320
- suggests that a 400 error is usually to do with `ALLOWED_HOSTS`.
347
+ [[ search- results-400-bad-request]]
348
+ .Search results for " django debug false 400 bad request"
349
+ image::images/search-results- 400-bad-request.png["Duckduckgo search results with stackoverflow and django docs"]
321
350
322
351
323
352
`ALLOWED_HOSTS` is a security setting
@@ -360,12 +389,13 @@ or on a server, so we'll use the `-e` flag again:
360
389
----
361
390
$ *docker build -t superlists . && \
362
391
docker run -p 8888:8888 -v ./src/db.sqlite3:/src/db.sqlite3 \
363
- -e DJANGO_SECRET_KEY=sekrit -e DJANGO_ALLOWED_HOST=localhost \
392
+ -e DJANGO_SECRET_KEY=sekrit \
393
+ -e DJANGO_ALLOWED_HOST=localhost \
364
394
-it superlists*
365
395
----
366
396
367
397
368
- ==== Collectstatic is required when Debug is Turned off
398
+ ==== Collectstatic is Required when Debug is Turned Off
369
399
370
400
An FT run (or just looking at the site) reveals that we've had a regression
371
401
in our static files.
@@ -381,7 +411,8 @@ FAILED (failures=1)
381
411
382
412
383
413
We saw this before when switching from the Django dev server to Gunicorn,
384
- so we introduced Whitenoise. Similarly, when we switch DEBUG off,
414
+ so we introduced Whitenoise.
415
+ Similarly, when we switch DEBUG off,
385
416
Whitenoise stops automagically finding static files in our code,
386
417
and instead we need to run `collectstatic`:
387
418
@@ -429,40 +460,40 @@ provoke a 500 error somehow and make sure we see tracebacks for it
429
460
A few things to think about when trying to prepare a production-ready configuration:
430
461
431
462
Don't use the Django dev server in production::
432
- Something like Gunicorn or uWSGI is a better tool for running Django;
433
- it will let you run multiple workers, for example.
434
- ((("Gunicorn", "benefits of")))
463
+ Something like Gunicorn or uWSGI is a better tool for running Django;
464
+ it will let you run multiple workers, for example.
465
+ ((("Gunicorn", "benefits of")))
435
466
436
467
Decide how to serve your static files::
437
468
Static files aren't the same kind of things as the dynamic content
438
469
that comes from Django and your webapp, so they need to be treated differently.
439
470
WhiteNoise is just one example of how you might do that.
440
471
441
472
Check your settings.py for dev-only settings::
442
- `DEBUG=True`, `ALLOWED_HOSTS` and `SECRET_KEY` are the ones we came across,
443
- but you will probably have others (we'll see more when we start to send
444
- emails from the server).
473
+ `DEBUG=True`, `ALLOWED_HOSTS` and `SECRET_KEY` are the ones we came across,
474
+ but you will probably have others
475
+ (we'll see more when we start to send emails from the server).
445
476
446
477
Change things one at a time and rerun your tests frequently::
447
- Whenever we make a change to our server configuration,
448
- we can rerun the test suite,
449
- and either be confident that everything works as well as it did before,
450
- or find out immediately if we did something wrong.
478
+ Whenever we make a change to our server configuration,
479
+ we can rerun the test suite,
480
+ and either be confident that everything works as well as it did before,
481
+ or find out immediately if we did something wrong.
451
482
452
483
Security::
453
- A serious discussion of server security is beyond the scope of this book,
454
- and I'd warn against running your own servers
455
- without learning a good bit more about it.
456
- (One reason people choose to use a PaaS to host their code
457
- is that it means a slightly fewer security issues to worry about.)
458
- If you'd like a place to start, here's as good a place as any:
459
- https://plusbryan.com/my-first-5-minutes-on-a-server-or-essential-security-for-linux-servers[My first 5 minutes on a server].
460
- I can definitely recommend the eye-opening experience of installing
461
- fail2ban and watching its logfiles to see just how quickly it picks up on
462
- random drive-by attempts to brute force your SSH login. The internet is a
463
- wild place!
464
- ((("security issues and settings", "server security")))
465
- ((("Platform-As-A-Service (PaaS)")))
484
+ A serious discussion of server security is beyond the scope of this book,
485
+ and I'd warn against running your own servers
486
+ without learning a good bit more about it.
487
+ (One reason people choose to use a PaaS to host their code
488
+ is that it means a slightly fewer security issues to worry about.)
489
+ If you'd like a place to start, here's as good a place as any:
490
+ https://plusbryan.com/my-first-5-minutes-on-a-server-or-essential-security-for-linux-servers[My first 5 minutes on a server].
491
+ I can definitely recommend the eye-opening experience of installing
492
+ fail2ban and watching its logfiles to see just how quickly it picks up on
493
+ random drive-by attempts to brute force your SSH login. The internet is a
494
+ wild place!
495
+ ((("security issues and settings", "server security")))
496
+ ((("Platform-As-A-Service (PaaS)")))
466
497
467
498
TODO: that last one probably belongs in the next chapter.
468
499
0 commit comments