1
+ import os
2
+
3
+ import docker
4
+ import pytest
5
+ import requests
6
+
7
+ from requests .packages .urllib3 .util .retry import Retry
8
+ from requests .adapters import HTTPAdapter
9
+
10
+
11
+ @pytest .fixture (scope = 'session' )
12
+ def http_client ():
13
+ """Requests session with retries and backoff."""
14
+ s = requests .Session ()
15
+ retries = Retry (total = 5 , backoff_factor = 1 )
16
+ s .mount ('http://' , HTTPAdapter (max_retries = retries ))
17
+ s .mount ('https://' , HTTPAdapter (max_retries = retries ))
18
+ return s
19
+
20
+
21
+ @pytest .fixture (scope = 'session' )
22
+ def docker_client ():
23
+ """Docker client configured based on the host environment"""
24
+ return docker .from_env ()
25
+
26
+
27
+ @pytest .fixture (scope = 'session' )
28
+ def image_name ():
29
+ """Image name to test"""
30
+ return 'sharptrick/sage-notebook'
31
+
32
+
33
+ class TrackedContainer (object ):
34
+ """Wrapper that collects docker container configuration and delays
35
+ container creation/execution.
36
+ Parameters
37
+ ----------
38
+ docker_client: docker.DockerClient
39
+ Docker client instance
40
+ image_name: str
41
+ Name of the docker image to launch
42
+ **kwargs: dict, optional
43
+ Default keyword arguments to pass to docker.DockerClient.containers.run
44
+ """
45
+ def __init__ (self , docker_client , image_name , ** kwargs ):
46
+ self .container = None
47
+ self .docker_client = docker_client
48
+ self .image_name = image_name
49
+ self .kwargs = kwargs
50
+
51
+ def run (self , ** kwargs ):
52
+ """Runs a docker container using the preconfigured image name
53
+ and a mix of the preconfigured container options and those passed
54
+ to this method.
55
+ Keeps track of the docker.Container instance spawned to kill it
56
+ later.
57
+ Parameters
58
+ ----------
59
+ **kwargs: dict, optional
60
+ Keyword arguments to pass to docker.DockerClient.containers.run
61
+ extending and/or overriding key/value pairs passed to the constructor
62
+ Returns
63
+ -------
64
+ docker.Container
65
+ """
66
+ all_kwargs = {}
67
+ all_kwargs .update (self .kwargs )
68
+ all_kwargs .update (kwargs )
69
+ self .container = self .docker_client .containers .run (self .image_name , ** all_kwargs )
70
+ return self .container
71
+
72
+ def remove (self ):
73
+ """Kills and removes the tracked docker container."""
74
+ if self .container :
75
+ self .container .remove (force = True )
76
+
77
+
78
+ @pytest .fixture (scope = 'function' )
79
+ def container (docker_client , image_name ):
80
+ """Notebook container with initial configuration appropriate for testing
81
+ (e.g., HTTP port exposed to the host for HTTP calls).
82
+ Yields the container instance and kills it when the caller is done with it.
83
+ """
84
+ container = TrackedContainer (
85
+ docker_client ,
86
+ image_name ,
87
+ detach = True ,
88
+ ports = {
89
+ '8888/tcp' : 8888
90
+ }
91
+ )
92
+ yield container
93
+ container .remove ()
0 commit comments