@@ -5,30 +5,72 @@ defmodule Containers do
5
5
alias Realtime.RateCounter
6
6
alias Realtime.Tenants.Migrations
7
7
8
- def initialize ( tenant ) do
9
- name = "realtime-test-#{ tenant . external_id } "
10
- % { port: port } = Database . from_tenant ( tenant , "realtime_test" , :stop )
8
+ use GenServer
11
9
12
- { _ , 0 } =
13
- System . cmd ( "docker" , [
14
- "run" ,
15
- "-d" ,
16
- "--name" ,
17
- name ,
18
- "-e" ,
19
- "POSTGRES_HOST=/var/run/postgresql" ,
20
- "-e" ,
21
- "POSTGRES_PASSWORD=postgres" ,
22
- "-p" ,
23
- "#{ port } :5432" ,
24
- "supabase/postgres:15.8.1.040" ,
25
- "postgres" ,
26
- "-c" ,
27
- "config_file=/etc/postgresql/postgresql.conf"
28
- ] )
10
+ @ image "supabase/postgres:15.8.1.040"
11
+
12
+ def start_container ( ) , do: GenServer . call ( __MODULE__ , :start_container , 10_000 )
13
+ def port ( ) , do: GenServer . call ( __MODULE__ , :port , 10_000 )
14
+
15
+ def start_link ( max_cases ) , do: GenServer . start_link ( __MODULE__ , max_cases , name: __MODULE__ )
16
+
17
+ def init ( max_cases ) do
18
+ existing_containers = existing_containers ( "realtime-test-*" )
19
+ ports = for { _ , port } <- existing_containers , do: port
20
+ available_ports = Enum . shuffle ( 5501 .. 9000 ) -- ports
21
+
22
+ { :ok , % { existing_containers: existing_containers , ports: available_ports } , { :continue , { :pool , max_cases } } }
23
+ end
24
+
25
+ def handle_continue ( { :pool , max_cases } , state ) do
26
+ { :ok , _pid } =
27
+ :poolboy . start_link (
28
+ [ name: { :local , Containers.Pool } , size: max_cases + 1 , max_overflow: 0 , worker_module: Containers.Container ] ,
29
+ [ ]
30
+ )
31
+
32
+ { :noreply , state }
33
+ end
34
+
35
+ def handle_call ( :port , _from , state ) do
36
+ [ port | ports ] = state . ports
37
+ { :reply , port , % { state | ports: ports } }
38
+ end
39
+
40
+ def handle_call ( :start_container , _from , state ) do
41
+ case state . existing_containers do
42
+ [ { name , port } | rest ] ->
43
+ { :reply , { :ok , name , port } , % { state | existing_containers: rest } }
44
+
45
+ [ ] ->
46
+ [ port | ports ] = state . ports
47
+ name = "realtime-test-#{ System . unique_integer ( [ :positive ] ) } "
48
+
49
+ docker_run! ( name , port )
50
+
51
+ { :reply , { :ok , name , port } , % { state | ports: ports } }
52
+ end
53
+ end
54
+
55
+ def initialize ( external_id ) do
56
+ name = "realtime-tenant-test-#{ external_id } "
57
+
58
+ port =
59
+ case existing_containers ( name ) do
60
+ [ { ^ name , port } ] ->
61
+ port
62
+
63
+ [ ] ->
64
+ port = 5500
65
+ docker_run! ( name , port )
66
+ port
67
+ end
29
68
30
69
check_container_ready ( name )
31
70
71
+ opts = % { external_id: external_id , name: external_id , port: port , jwt_secret: "secure_jwt_secret" }
72
+ tenant = Generators . tenant_fixture ( opts )
73
+
32
74
Migrations . run_migrations ( tenant )
33
75
{ :ok , pid } = Database . connect ( tenant , "realtime_test" , :stop )
34
76
:ok = Migrations . create_partitions ( pid )
@@ -39,10 +81,10 @@ defmodule Containers do
39
81
40
82
@ doc "Return port for a container that can be used"
41
83
def checkout ( ) do
42
- with container when is_pid ( container ) <- :poolboy . checkout ( Containers , true , 5_000 ) ,
84
+ with container when is_pid ( container ) <- :poolboy . checkout ( Containers.Pool , true , 5_000 ) ,
43
85
port <- Container . port ( container ) do
44
86
# Automatically checkin the container at the end of the test
45
- ExUnit.Callbacks . on_exit ( fn -> :poolboy . checkin ( Containers , container ) end )
87
+ ExUnit.Callbacks . on_exit ( fn -> :poolboy . checkin ( Containers.Pool , container ) end )
46
88
47
89
{ :ok , port }
48
90
else
@@ -52,13 +94,13 @@ defmodule Containers do
52
94
53
95
# Might be worth changing this to {:ok, tenant}
54
96
def checkout_tenant ( opts \\ [ ] ) do
55
- with container when is_pid ( container ) <- :poolboy . checkout ( Containers , true , 5_000 ) ,
97
+ with container when is_pid ( container ) <- :poolboy . checkout ( Containers.Pool , true , 5_000 ) ,
56
98
port <- Container . port ( container ) do
57
99
tenant = Generators . tenant_fixture ( % { port: port , migrations_ran: 0 } )
58
100
run_migrations? = Keyword . get ( opts , :run_migrations , false )
59
101
60
102
# Automatically checkin the container at the end of the test
61
- ExUnit.Callbacks . on_exit ( fn -> :poolboy . checkin ( Containers , container ) end )
103
+ ExUnit.Callbacks . on_exit ( fn -> :poolboy . checkin ( Containers.Pool , container ) end )
62
104
63
105
settings = Database . from_tenant ( tenant , "realtime_test" , :stop )
64
106
settings = % { settings | max_restarts: 0 , ssl: false }
@@ -111,6 +153,30 @@ defmodule Containers do
111
153
end
112
154
end
113
155
156
+ def stop_container ( external_id ) do
157
+ name = "realtime-tenant-test-#{ external_id } "
158
+ System . cmd ( "docker" , [ "rm" , "-f" , name ] )
159
+ end
160
+
161
+ defp existing_containers ( pattern ) do
162
+ { containers , 0 } = System . cmd ( "docker" , [ "ps" , "-a" , "--format" , "{{json .}}" , "--filter" , "name=#{ pattern } " ] )
163
+
164
+ containers
165
+ |> String . split ( "\n " , trim: true )
166
+ |> Enum . map ( fn container ->
167
+ container = Jason . decode! ( container )
168
+ # Ports" => "0.0.0.0:6445->5432/tcp, [::]:6445->5432/tcp"
169
+ regex = ~r/ (?<=:)\d +(?=->)/
170
+
171
+ [ port ] =
172
+ Regex . scan ( regex , container [ "Ports" ] )
173
+ |> List . flatten ( )
174
+ |> Enum . uniq ( )
175
+
176
+ { container [ "Names" ] , String . to_integer ( port ) }
177
+ end )
178
+ end
179
+
114
180
defp check_container_ready ( name , attempts \\ 50 )
115
181
defp check_container_ready ( name , 0 ) , do: raise ( "Container #{ name } is not ready" )
116
182
@@ -155,4 +221,24 @@ defmodule Containers do
155
221
end
156
222
end )
157
223
end
224
+
225
+ defp docker_run! ( name , port ) do
226
+ { _ , 0 } =
227
+ System . cmd ( "docker" , [
228
+ "run" ,
229
+ "-d" ,
230
+ "--name" ,
231
+ name ,
232
+ "-e" ,
233
+ "POSTGRES_HOST=/var/run/postgresql" ,
234
+ "-e" ,
235
+ "POSTGRES_PASSWORD=postgres" ,
236
+ "-p" ,
237
+ "#{ port } :5432" ,
238
+ @ image ,
239
+ "postgres" ,
240
+ "-c" ,
241
+ "config_file=/etc/postgresql/postgresql.conf"
242
+ ] )
243
+ end
158
244
end
0 commit comments