1
+ #
2
+ # Copyright IBM Corp. 2016 All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ import os
18
+ import time
19
+ import re
20
+
21
+ from bdd_test_util import cli_call
22
+
23
+ REST_PORT = "7050"
24
+
25
+ class ContainerData :
26
+ def __init__ (self , containerName , ipAddress , envFromInspect , composeService ):
27
+ self .containerName = containerName
28
+ self .ipAddress = ipAddress
29
+ self .envFromInspect = envFromInspect
30
+ self .composeService = composeService
31
+
32
+ def getEnv (self , key ):
33
+ envValue = None
34
+ for val in self .envFromInspect :
35
+ if val .startswith (key ):
36
+ envValue = val [len (key ):]
37
+ break
38
+ if envValue == None :
39
+ raise Exception ("ENV key not found ({0}) for container ({1})" .format (key , self .containerName ))
40
+ return envValue
41
+
42
+ def getDockerComposeFileArgsFromYamlFile (compose_yaml ):
43
+ parts = compose_yaml .split ()
44
+ args = []
45
+ for part in parts :
46
+ args = args + ["-f" ] + [part ]
47
+ return args
48
+
49
+ def parseComposeOutput (context ):
50
+ """Parses the compose output results and set appropriate values into context. Merges existing with newly composed."""
51
+ # Use the prefix to get the container name
52
+ containerNamePrefix = os .path .basename (os .getcwd ()) + "_"
53
+ containerNames = []
54
+ for l in context .compose_error .splitlines ():
55
+ tokens = l .split ()
56
+ print (tokens )
57
+ if 1 < len (tokens ):
58
+ thisContainer = tokens [1 ]
59
+ if containerNamePrefix not in thisContainer :
60
+ thisContainer = containerNamePrefix + thisContainer + "_1"
61
+ if thisContainer not in containerNames :
62
+ containerNames .append (thisContainer )
63
+
64
+ print ("Containers started: " )
65
+ print (containerNames )
66
+ # Now get the Network Address for each name, and set the ContainerData onto the context.
67
+ containerDataList = []
68
+ for containerName in containerNames :
69
+ output , error , returncode = \
70
+ cli_call (context , ["docker" , "inspect" , "--format" , "{{ .NetworkSettings.IPAddress }}" , containerName ], expect_success = True )
71
+ print ("container {0} has address = {1}" .format (containerName , output .splitlines ()[0 ]))
72
+ ipAddress = output .splitlines ()[0 ]
73
+
74
+ # Get the environment array
75
+ output , error , returncode = \
76
+ cli_call (context , ["docker" , "inspect" , "--format" , "{{ .Config.Env }}" , containerName ], expect_success = True )
77
+ env = output .splitlines ()[0 ][1 :- 1 ].split ()
78
+
79
+ # Get the Labels to access the com.docker.compose.service value
80
+ output , error , returncode = \
81
+ cli_call (context , ["docker" , "inspect" , "--format" , "{{ .Config.Labels }}" , containerName ], expect_success = True )
82
+ labels = output .splitlines ()[0 ][4 :- 1 ].split ()
83
+ dockerComposeService = [composeService [27 :] for composeService in labels if composeService .startswith ("com.docker.compose.service:" )][0 ]
84
+ print ("dockerComposeService = {0}" .format (dockerComposeService ))
85
+ print ("container {0} has env = {1}" .format (containerName , env ))
86
+ containerDataList .append (ContainerData (containerName , ipAddress , env , dockerComposeService ))
87
+ # Now merge the new containerData info with existing
88
+ newContainerDataList = []
89
+ if "compose_containers" in context :
90
+ # Need to merge I new list
91
+ newContainerDataList = context .compose_containers
92
+ newContainerDataList = newContainerDataList + containerDataList
93
+
94
+ setattr (context , "compose_containers" , newContainerDataList )
95
+ print ("" )
96
+
97
+ def allContainersAreReadyWithinTimeout (context , timeout ):
98
+ timeoutTimestamp = time .time () + timeout
99
+ formattedTime = time .strftime ("%X" , time .localtime (timeoutTimestamp ))
100
+ print ("All containers should be up by {}" .format (formattedTime ))
101
+
102
+ for container in context .compose_containers :
103
+ if not containerIsReadyByTimestamp (container , timeoutTimestamp ):
104
+ return False
105
+
106
+ print ("All containers in ready state, ready to proceed" )
107
+ return True
108
+
109
+ def containerIsReadyByTimestamp (container , timeoutTimestamp ):
110
+ while containerIsNotReady (container ):
111
+ if timestampExceeded (timeoutTimestamp ):
112
+ print ("Timed out waiting for {}" .format (container .containerName ))
113
+ return False
114
+
115
+ print ("{} not ready, waiting..." .format (container .containerName ))
116
+ time .sleep (1 )
117
+
118
+ print ("{} now available" .format (container .containerName ))
119
+ return True
120
+
121
+ def timestampExceeded (timeoutTimestamp ):
122
+ return time .time () > timeoutTimestamp
123
+
124
+ def containerIsNotReady (container ):
125
+ return not containerIsReady (container )
126
+
127
+ def containerIsReady (container ):
128
+ isReady = tcpPortsAreReady (container )
129
+ isReady = isReady and restPortRespondsIfContainerIsPeer (container )
130
+
131
+ return isReady
132
+
133
+ def tcpPortsAreReady (container ):
134
+ netstatOutput = getContainerNetstatOutput (container .containerName )
135
+
136
+ for line in netstatOutput .splitlines ():
137
+ if re .search ("ESTABLISHED|LISTEN" , line ):
138
+ return True
139
+
140
+ print ("No TCP connections are ready in container {}" .format (container .containerName ))
141
+ return False
142
+
143
+ def getContainerNetstatOutput (containerName ):
144
+ command = ["docker" , "exec" , containerName , "netstat" , "-atun" ]
145
+ stdout , stderr , returnCode = cli_call (None , command , expect_success = False )
146
+
147
+ return stdout
148
+
149
+ def restPortRespondsIfContainerIsPeer (container ):
150
+ containerName = container .containerName
151
+ command = ["docker" , "exec" , containerName , "curl" , "localhost:" + REST_PORT ]
152
+
153
+ if containerIsPeer (container ):
154
+ stdout , stderr , returnCode = cli_call (None , command , expect_success = False )
155
+
156
+ if returnCode != 0 :
157
+ print ("Connection to REST Port on {} failed" .format (containerName ))
158
+
159
+ return returnCode == 0
160
+
161
+ return True
162
+
163
+ def containerIsPeer (container ):
164
+ netstatOutput = getContainerNetstatOutput (container .containerName )
165
+
166
+ for line in netstatOutput .splitlines ():
167
+ if re .search (REST_PORT , line ) and re .search ("LISTEN" , line ):
168
+ return True
169
+
170
+ return False
0 commit comments