2
2
3
3
"""Documentation Builder Environments."""
4
4
5
- from __future__ import absolute_import
6
- from builtins import str
7
- from builtins import object
5
+ from __future__ import (
6
+ absolute_import , division , print_function , unicode_literals )
7
+
8
+ import logging
8
9
import os
9
10
import re
10
- import sys
11
- import logging
11
+ import socket
12
12
import subprocess
13
+ import sys
13
14
import traceback
14
- import socket
15
15
from datetime import datetime
16
16
17
+ import six
18
+ from builtins import object , str
17
19
from django .conf import settings
18
20
from django .utils .translation import ugettext_lazy as _
19
21
from docker import APIClient
20
- from docker .errors import APIError as DockerAPIError , DockerException
22
+ from docker .errors import APIError as DockerAPIError
23
+ from docker .errors import DockerException
24
+ from requests .exceptions import ConnectionError
21
25
from slumber .exceptions import HttpClientError
22
26
23
27
from readthedocs .builds .constants import BUILD_STATE_FINISHED
24
28
from readthedocs .builds .models import BuildCommandResultMixin
25
29
from readthedocs .core .utils import slugify
26
30
from readthedocs .projects .constants import LOG_TEMPLATE
27
31
from readthedocs .restapi .client import api as api_v2
28
- from requests .exceptions import ConnectionError
29
32
30
- from .exceptions import (BuildEnvironmentException , BuildEnvironmentError ,
31
- BuildEnvironmentWarning , BuildEnvironmentCreationFailed , VersionLockedError , ProjectBuildsSkippedError , YAMLParseError , BuildTimeoutError )
32
- from .constants import (DOCKER_SOCKET , DOCKER_VERSION , DOCKER_IMAGE ,
33
- DOCKER_LIMITS , DOCKER_TIMEOUT_EXIT_CODE ,
34
- DOCKER_OOM_EXIT_CODE , SPHINX_TEMPLATE_DIR ,
35
- MKDOCS_TEMPLATE_DIR , DOCKER_HOSTNAME_MAX_LEN )
36
- import six
33
+ from .constants import (
34
+ DOCKER_HOSTNAME_MAX_LEN , DOCKER_IMAGE , DOCKER_LIMITS , DOCKER_OOM_EXIT_CODE ,
35
+ DOCKER_SOCKET , DOCKER_TIMEOUT_EXIT_CODE , DOCKER_VERSION ,
36
+ MKDOCS_TEMPLATE_DIR , SPHINX_TEMPLATE_DIR )
37
+ from .exceptions import (
38
+ BuildEnvironmentCreationFailed , BuildEnvironmentError ,
39
+ BuildEnvironmentException , BuildEnvironmentWarning , BuildTimeoutError ,
40
+ ProjectBuildsSkippedError , VersionLockedError , YAMLParseError )
37
41
38
42
log = logging .getLogger (__name__ )
39
43
40
-
41
44
__all__ = (
42
45
'api_v2' ,
43
46
'BuildCommand' ,
@@ -220,8 +223,12 @@ def run(self):
220
223
:type cmd_input: str
221
224
:param combine_output: combine STDERR into STDOUT
222
225
"""
223
- log .info ("Running in container %s: '%s' [%s]" ,
224
- self .build_env .container_id , self .get_command (), self .cwd )
226
+ log .info (
227
+ "Running in container %s: '%s' [%s]" ,
228
+ self .build_env .container_id ,
229
+ self .get_command (),
230
+ self .cwd ,
231
+ )
225
232
226
233
self .start_time = datetime .utcnow ()
227
234
client = self .build_env .get_client ()
@@ -230,7 +237,7 @@ def run(self):
230
237
container = self .build_env .container_id ,
231
238
cmd = self .get_wrapped_command (),
232
239
stdout = True ,
233
- stderr = True
240
+ stderr = True ,
234
241
)
235
242
236
243
output = client .exec_start (exec_id = exec_cmd ['Id' ], stream = False )
@@ -439,10 +446,13 @@ def __enter__(self):
439
446
def __exit__ (self , exc_type , exc_value , tb ):
440
447
ret = self .handle_exception (exc_type , exc_value , tb )
441
448
self .update_build (BUILD_STATE_FINISHED )
442
- log .info (LOG_TEMPLATE
443
- .format (project = self .project .slug ,
444
- version = self .version .slug ,
445
- msg = 'Build finished' ))
449
+ log .info (
450
+ LOG_TEMPLATE .format (
451
+ project = self .project .slug ,
452
+ version = self .version .slug ,
453
+ msg = 'Build finished' ,
454
+ )
455
+ )
446
456
return ret
447
457
448
458
def handle_exception (self , exc_type , exc_value , _ ):
@@ -479,7 +489,8 @@ def handle_exception(self, exc_type, exc_value, _):
479
489
'project' : self .project .slug ,
480
490
'version' : self .version .slug ,
481
491
},
482
- })
492
+ },
493
+ )
483
494
self .failure = exc_value
484
495
return True
485
496
@@ -488,11 +499,13 @@ def record_command(self, command):
488
499
489
500
def _log_warning (self , msg ):
490
501
# :'(
491
- log .warning (LOG_TEMPLATE .format (
492
- project = self .project .slug ,
493
- version = self .version .slug ,
494
- msg = msg ,
495
- ))
502
+ log .warning (
503
+ LOG_TEMPLATE .format (
504
+ project = self .project .slug ,
505
+ version = self .version .slug ,
506
+ msg = msg ,
507
+ )
508
+ )
496
509
497
510
def run (self , * cmd , ** kwargs ):
498
511
kwargs .update ({
@@ -550,15 +563,18 @@ def update_build(self, state=None):
550
563
551
564
# TODO drop exit_code and provide a more meaningful UX for error
552
565
# reporting
553
- if self .failure and isinstance (self .failure ,
554
- BuildEnvironmentException ):
566
+ if self .failure and isinstance (
567
+ self .failure ,
568
+ BuildEnvironmentException ,
569
+ ):
555
570
self .build ['exit_code' ] = self .failure .status_code
556
571
elif self .commands :
557
- self .build ['exit_code' ] = max ([cmd .exit_code
558
- for cmd in self .commands ])
572
+ self .build ['exit_code' ] = max ([
573
+ cmd .exit_code for cmd in self .commands
574
+ ])
559
575
560
- self .build ['setup' ] = self .build ['setup_error' ] = ""
561
- self .build ['output' ] = self .build ['error' ] = ""
576
+ self .build ['setup' ] = self .build ['setup_error' ] = ''
577
+ self .build ['output' ] = self .build ['error' ] = ''
562
578
563
579
if self .start_time :
564
580
build_length = (datetime .utcnow () - self .start_time )
@@ -567,9 +583,13 @@ def update_build(self, state=None):
567
583
if self .failure is not None :
568
584
# Surface a generic error if the class is not a
569
585
# BuildEnvironmentError
570
- if not isinstance (self .failure ,
571
- (BuildEnvironmentException ,
572
- BuildEnvironmentWarning )):
586
+ if not isinstance (
587
+ self .failure ,
588
+ (
589
+ BuildEnvironmentException ,
590
+ BuildEnvironmentWarning ,
591
+ ),
592
+ ):
573
593
log .error (
574
594
'Build failed with unhandled exception: %s' ,
575
595
str (self .failure ),
@@ -585,7 +605,7 @@ def update_build(self, state=None):
585
605
self .failure = BuildEnvironmentError (
586
606
BuildEnvironmentError .GENERIC_WITH_BUILD_ID .format (
587
607
build_id = self .build ['id' ],
588
- )
608
+ ),
589
609
)
590
610
self .build ['error' ] = str (self .failure )
591
611
@@ -608,11 +628,11 @@ def update_build(self, state=None):
608
628
api_v2 .build (self .build ['id' ]).put (self .build )
609
629
except HttpClientError as e :
610
630
log .exception (
611
- " Unable to update build: id=%d" ,
631
+ ' Unable to update build: id=%d' ,
612
632
self .build ['id' ],
613
633
)
614
634
except Exception :
615
- log .exception (" Unknown build exception" )
635
+ log .exception (' Unknown build exception' )
616
636
617
637
618
638
class LocalBuildEnvironment (BuildEnvironment ):
@@ -653,7 +673,7 @@ def __init__(self, *args, **kwargs):
653
673
build = self .build .get ('id' ),
654
674
project_id = self .project .pk ,
655
675
project_name = self .project .slug ,
656
- )[:DOCKER_HOSTNAME_MAX_LEN ]
676
+ )[:DOCKER_HOSTNAME_MAX_LEN ],
657
677
)
658
678
if self .config and self .config .build_image :
659
679
self .container_image = self .config .build_image
@@ -675,18 +695,25 @@ def __enter__(self):
675
695
if state is not None :
676
696
if state .get ('Running' ) is True :
677
697
exc = BuildEnvironmentError (
678
- _ ('A build environment is currently '
679
- 'running for this version' ))
698
+ _ (
699
+ 'A build environment is currently '
700
+ 'running for this version' ,
701
+ ),
702
+ )
680
703
self .failure = exc
681
704
self .build ['state' ] = BUILD_STATE_FINISHED
682
705
raise exc
683
706
else :
684
- log .warning (LOG_TEMPLATE
685
- .format (
686
- project = self .project .slug ,
687
- version = self .version .slug ,
688
- msg = ("Removing stale container {0}"
689
- .format (self .container_id ))))
707
+ log .warning (
708
+ LOG_TEMPLATE .format (
709
+ project = self .project .slug ,
710
+ version = self .version .slug ,
711
+ msg = (
712
+ 'Removing stale container {0}'
713
+ .format (self .container_id )
714
+ ),
715
+ )
716
+ )
690
717
client = self .get_client ()
691
718
client .remove_container (self .container_id )
692
719
except (DockerAPIError , ConnectionError ):
@@ -731,8 +758,7 @@ def __exit__(self, exc_type, exc_value, tb):
731
758
# request. These errors should not surface to the user.
732
759
except (DockerAPIError , ConnectionError ):
733
760
log .exception (
734
- LOG_TEMPLATE
735
- .format (
761
+ LOG_TEMPLATE .format (
736
762
project = self .project .slug ,
737
763
version = self .version .slug ,
738
764
msg = "Couldn't remove container" ,
@@ -772,7 +798,7 @@ def get_client(self):
772
798
raise BuildEnvironmentError (
773
799
BuildEnvironmentError .GENERIC_WITH_BUILD_ID .format (
774
800
build_id = self .build ['id' ],
775
- )
801
+ ),
776
802
)
777
803
778
804
def get_container_host_config (self ):
@@ -851,14 +877,18 @@ def update_build_from_container_state(self):
851
877
if state is not None and state .get ('Running' ) is False :
852
878
if state .get ('ExitCode' ) == DOCKER_TIMEOUT_EXIT_CODE :
853
879
self .failure = BuildEnvironmentError (
854
- _ ('Build exited due to time out' ))
880
+ _ ('Build exited due to time out' ),
881
+ )
855
882
elif state .get ('OOMKilled' , False ):
856
883
self .failure = BuildEnvironmentError (
857
- _ ('Build exited due to excessive memory consumption' ))
884
+ _ ('Build exited due to excessive memory consumption' ),
885
+ )
858
886
elif state .get ('Error' ):
859
- self .failure = BuildEnvironmentError (
860
- (_ ('Build exited due to unknown error: {0}' )
861
- .format (state .get ('Error' ))))
887
+ self .failure = BuildEnvironmentError ((
888
+ _ ('Build exited due to unknown error: {0}' )
889
+ .format (state .get ('Error' ))
890
+ ),
891
+ )
862
892
863
893
def create_container (self ):
864
894
"""Create docker container."""
@@ -870,9 +900,12 @@ def create_container(self):
870
900
)
871
901
self .container = client .create_container (
872
902
image = self .container_image ,
873
- command = ('/bin/sh -c "sleep {time}; exit {exit}"'
874
- .format (time = self .container_time_limit ,
875
- exit = DOCKER_TIMEOUT_EXIT_CODE )),
903
+ command = (
904
+ '/bin/sh -c "sleep {time}; exit {exit}"' .format (
905
+ time = self .container_time_limit ,
906
+ exit = DOCKER_TIMEOUT_EXIT_CODE ,
907
+ )
908
+ ),
876
909
name = self .container_id ,
877
910
hostname = self .container_id ,
878
911
host_config = self .get_container_host_config (),
@@ -897,12 +930,11 @@ def create_container(self):
897
930
raise BuildEnvironmentError (
898
931
BuildEnvironmentError .GENERIC_WITH_BUILD_ID .format (
899
932
build_id = self .build ['id' ],
900
- )
933
+ ),
901
934
)
902
935
except DockerAPIError as e :
903
936
log .exception (
904
- LOG_TEMPLATE
905
- .format (
937
+ LOG_TEMPLATE .format (
906
938
project = self .project .slug ,
907
939
version = self .version .slug ,
908
940
msg = e .explanation ,
0 commit comments