github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/bddtests/steps/compose.py (about) 1 # Copyright IBM Corp. 2016 All Rights Reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # 15 16 import os 17 import uuid 18 import bdd_test_util 19 from contexthelper import ContextHelper 20 import json 21 22 from abc import ABCMeta, abstractmethod 23 24 class ContainerData: 25 def __init__(self, containerName, ipAddress, envFromInspect, composeService, ports): 26 self.containerName = containerName 27 self.ipAddress = ipAddress 28 self.envFromInspect = envFromInspect 29 self.composeService = composeService 30 self.ports = ports 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 43 class CompositionCallback: 44 __metaclass__ = ABCMeta 45 @abstractmethod 46 def composing(self, composition, context): 47 pass 48 @abstractmethod 49 def decomposing(self, composition, context): 50 pass 51 @abstractmethod 52 def getEnv(self, composition, context, env): 53 pass 54 55 class Test(CompositionCallback): 56 def composing(self, composition, context): 57 pass 58 def decomposing(self, composition, context): 59 pass 60 def getEnv(self, composition, context, env): 61 pass 62 63 def GetDockerSafeUUID(): 64 return str(uuid.uuid1()).replace('-','') 65 66 class Composition: 67 68 @classmethod 69 def RegisterCallbackInContext(cls, context, callback): 70 if not isinstance(callback, CompositionCallback): 71 raise TypeError("Expected type to be {0}, instead received {1}".format(CompositionCallback, type(callback))) 72 Composition.GetCompositionCallbacksFromContext(context).append(callback) 73 74 @classmethod 75 def GetCompositionCallbacksFromContext(cls, context): 76 if not "compositionCallbacks" in context: 77 context.compositionCallbacks = [] 78 return context.compositionCallbacks 79 80 81 @classmethod 82 def GetUUID(cls): 83 return GetDockerSafeUUID() 84 85 def __init__(self, context, composeFilesYaml, projectName=None, 86 force_recreate=True, components=[], register_and_up=True): 87 self.contextHelper = ContextHelper.GetHelper(context=context) 88 if not projectName: 89 projectName = self.contextHelper.getGuuid() 90 self.projectName = projectName 91 self.context = context 92 self.containerDataList = [] 93 self.composeFilesYaml = composeFilesYaml 94 self.serviceNames = [] 95 self.serviceNames = self._collectServiceNames() 96 if register_and_up: 97 # Register with contextHelper (Supports docgen) 98 self.contextHelper.registerComposition(self) 99 [callback.composing(self, context) for callback in Composition.GetCompositionCallbacksFromContext(context)] 100 self.up(context, force_recreate, components) 101 102 def _collectServiceNames(self): 103 'First collect the services names.' 104 servicesList = [service for service in self.issueCommand(["config", "--services"]).splitlines() if "WARNING" not in service] 105 return servicesList 106 107 def up(self, context, force_recreate=True, components=[]): 108 self.serviceNames = self._collectServiceNames() 109 command = ["up", "-d"] 110 if force_recreate: 111 command += ["--force-recreate"] 112 self.issueCommand(command + components) 113 114 def scale(self, context, serviceName, count=1): 115 self.serviceNames = self._collectServiceNames() 116 command = ["scale", "%s=%d" %(serviceName, count)] 117 self.issueCommand(command) 118 119 def stop(self, context, components=[]): 120 self.serviceNames = self._collectServiceNames() 121 command = ["stop"] 122 self.issueCommand(command, components) 123 124 def start(self, context, components=[]): 125 self.serviceNames = self._collectServiceNames() 126 command = ["start"] 127 self.issueCommand(command, components) 128 129 def getServiceNames(self): 130 return list(self.serviceNames) 131 132 def parseComposeFilesArg(self, composeFileArgs): 133 args = [arg for sublist in [["-f", file] for file in [file if not os.path.isdir(file) else os.path.join(file, 'docker-compose.yml') for file in composeFileArgs.split()]] for arg in sublist] 134 return args 135 136 def getFileArgs(self): 137 return self.parseComposeFilesArg(self.composeFilesYaml) 138 139 def getEnvAdditions(self): 140 myEnv = {} 141 myEnv["COMPOSE_PROJECT_NAME"] = self.projectName 142 myEnv["CORE_PEER_NETWORKID"] = self.projectName 143 # Invoke callbacks 144 [callback.getEnv(self, self.context, myEnv) for callback in Composition.GetCompositionCallbacksFromContext(self.context)] 145 return myEnv 146 147 def getEnv(self): 148 myEnv = os.environ.copy() 149 for key,value in self.getEnvAdditions().iteritems(): 150 myEnv[key] = value 151 # myEnv["COMPOSE_PROJECT_NAME"] = self.projectName 152 # myEnv["CORE_PEER_NETWORKID"] = self.projectName 153 # # Invoke callbacks 154 # [callback.getEnv(self, self.context, myEnv) for callback in Composition.GetCompositionCallbacksFromContext(self.context)] 155 return myEnv 156 157 def getConfig(self): 158 return self.issueCommand(["config"]) 159 160 def refreshContainerIDs(self): 161 containers = self.issueCommand(["ps", "-q"]).split() 162 return containers 163 164 def _callCLI(self, argList, expect_success, env): 165 return bdd_test_util.cli_call(argList, expect_success=expect_success, env=env) 166 167 def issueCommand(self, command, components=[]): 168 componentList = [] 169 useCompose = True 170 for component in components: 171 if '_' in component: 172 useCompose = False 173 componentList.append("%s_%s" % (self.projectName, component)) 174 else: 175 break 176 177 # If we need to perform an operation on a specific container, use 178 # docker not docker-compose 179 if useCompose: 180 cmdArgs = self.getFileArgs()+ command + components 181 cmd = ["docker-compose"] + cmdArgs 182 else: 183 cmdArgs = command + componentList 184 cmd = ["docker"] + cmdArgs 185 186 #print("cmd:", cmd) 187 output, error, returncode = \ 188 self._callCLI(cmd, expect_success=True, env=self.getEnv()) 189 190 # Don't rebuild if ps command 191 if command[0] !="ps" and command[0] !="config": 192 self.rebuildContainerData() 193 return output 194 195 def rebuildContainerData(self): 196 self.containerDataList[:] = [] 197 for containerID in self.refreshContainerIDs(): 198 199 # get container metadata 200 container = json.loads(bdd_test_util.cli_call(["docker", "inspect", containerID], expect_success=True)[0])[0] 201 202 # container name 203 container_name = container['Name'][1:] 204 205 # container ip address (only if container is running) 206 container_ipaddress = None 207 if container['State']['Running']: 208 container_ipaddress = container['NetworkSettings']['IPAddress'] 209 if not container_ipaddress: 210 # ipaddress not found at the old location, try the new location 211 container_ipaddress = container['NetworkSettings']['Networks'].values()[0]['IPAddress'] 212 213 # container environment 214 container_env = container['Config']['Env'] 215 216 # container exposed ports 217 container_ports = container['NetworkSettings']['Ports'] 218 219 # container docker-compose service 220 container_compose_service = container['Config']['Labels']['com.docker.compose.service'] 221 222 self.containerDataList.append(ContainerData(container_name, container_ipaddress, container_env, container_compose_service, container_ports)) 223 224 def decompose(self): 225 self.issueCommand(["unpause"]) 226 self.issueCommand(["down"]) 227 self.issueCommand(["kill"]) 228 self.issueCommand(["rm", "-f"]) 229 230 # Now remove associated chaincode containers if any 231 output, error, returncode = \ 232 bdd_test_util.cli_call(["docker"] + ["ps", "-qa", "--filter", "name={0}".format(self.projectName)], expect_success=True, env=self.getEnv()) 233 for containerId in output.splitlines(): 234 output, error, returncode = \ 235 bdd_test_util.cli_call(["docker"] + ["rm", "-f", containerId], expect_success=True, env=self.getEnv()) 236 237 # Remove the associated network 238 output, error, returncode = \ 239 bdd_test_util.cli_call(["docker"] + ["network", "ls", "-q", "--filter", "name={0}".format(self.projectName)], expect_success=True, env=self.getEnv()) 240 for networkId in output.splitlines(): 241 output, error, returncode = \ 242 bdd_test_util.cli_call(["docker"] + ["network", "rm", networkId], expect_success=True, env=self.getEnv()) 243 244 # Invoke callbacks 245 [callback.decomposing(self, self.context) for callback in Composition.GetCompositionCallbacksFromContext(self.context)]