github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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 peer_basic_impl 21 import json 22 23 from abc import ABCMeta, abstractmethod 24 25 class CompositionCallback: 26 __metaclass__ = ABCMeta 27 @abstractmethod 28 def composing(self, composition, context): 29 pass 30 @abstractmethod 31 def decomposing(self, composition, context): 32 pass 33 @abstractmethod 34 def getEnv(self, composition, context, env): 35 pass 36 37 class Test(CompositionCallback): 38 def composing(self, composition, context): 39 pass 40 def decomposing(self, composition, context): 41 pass 42 def getEnv(self, composition, context, env): 43 pass 44 45 def GetDockerSafeUUID(): 46 return str(uuid.uuid1()).replace('-','') 47 48 class Composition: 49 50 @classmethod 51 def RegisterCallbackInContext(cls, context, callback): 52 if not isinstance(callback, CompositionCallback): 53 raise TypeError("Expected type to be {0}, instead received {1}".format(CompositionCallback, type(callback))) 54 Composition.GetCompositionCallbacksFromContext(context).append(callback) 55 56 @classmethod 57 def GetCompositionCallbacksFromContext(cls, context): 58 if not "compositionCallbacks" in context: 59 context.compositionCallbacks = [] 60 return context.compositionCallbacks 61 62 63 @classmethod 64 def GetUUID(cls): 65 return GetDockerSafeUUID() 66 67 def __init__(self, context, composeFilesYaml, projectName = None, 68 force_recreate = True, components = []): 69 self.contextHelper = ContextHelper.GetHelper(context=context) 70 if not projectName: 71 projectName = self.contextHelper.getGuuid() 72 self.projectName = projectName 73 self.context = context 74 self.containerDataList = [] 75 self.composeFilesYaml = composeFilesYaml 76 77 self.serviceNames = [] 78 self.serviceNames = self._collectServiceNames() 79 [callback.composing(self, context) for callback in Composition.GetCompositionCallbacksFromContext(context)] 80 self.up(context, force_recreate, components) 81 82 def _collectServiceNames(self): 83 'First collect the services names.' 84 servicesList = [service for service in self.issueCommand(["config", "--services"]).splitlines() if "WARNING" not in service] 85 return servicesList 86 87 def up(self, context, force_recreate=True, components=[]): 88 self.serviceNames = self._collectServiceNames() 89 command = ["up", "-d"] 90 if force_recreate: 91 command += ["--force-recreate"] 92 self.issueCommand(command + components) 93 94 def scale(self, context, serviceName, count=1): 95 self.serviceNames = self._collectServiceNames() 96 command = ["scale", "%s=%d" %(serviceName, count)] 97 self.issueCommand(command) 98 99 def stop(self, context, components=[]): 100 self.serviceNames = self._collectServiceNames() 101 command = ["stop"] 102 self.issueCommand(command, components) 103 104 def start(self, context, components=[]): 105 self.serviceNames = self._collectServiceNames() 106 command = ["start"] 107 self.issueCommand(command, components) 108 109 def getServiceNames(self): 110 return list(self.serviceNames) 111 112 def parseComposeFilesArg(self, composeFileArgs): 113 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] 114 return args 115 116 def getFileArgs(self): 117 return self.parseComposeFilesArg(self.composeFilesYaml) 118 119 def getEnvAdditions(self): 120 myEnv = {} 121 myEnv["COMPOSE_PROJECT_NAME"] = self.projectName 122 myEnv["CORE_PEER_NETWORKID"] = self.projectName 123 # Invoke callbacks 124 [callback.getEnv(self, self.context, myEnv) for callback in Composition.GetCompositionCallbacksFromContext(self.context)] 125 return myEnv 126 127 def getEnv(self): 128 myEnv = os.environ.copy() 129 for key,value in self.getEnvAdditions().iteritems(): 130 myEnv[key] = value 131 # myEnv["COMPOSE_PROJECT_NAME"] = self.projectName 132 # myEnv["CORE_PEER_NETWORKID"] = self.projectName 133 # # Invoke callbacks 134 # [callback.getEnv(self, self.context, myEnv) for callback in Composition.GetCompositionCallbacksFromContext(self.context)] 135 return myEnv 136 137 def getConfig(self): 138 return self.issueCommand(["config"]) 139 140 def refreshContainerIDs(self): 141 containers = self.issueCommand(["ps", "-q"]).split() 142 return containers 143 144 def _callCLI(self, argList, expect_success, env): 145 return bdd_test_util.cli_call(argList, expect_success=expect_success, env=env) 146 147 def issueCommand(self, command, components=[]): 148 componentList = [] 149 useCompose = True 150 for component in components: 151 if '_' in component: 152 useCompose = False 153 componentList.append("%s_%s" % (self.projectName, component)) 154 else: 155 break 156 157 # If we need to perform an operation on a specific container, use 158 # docker not docker-compose 159 if useCompose: 160 cmdArgs = self.getFileArgs()+ command + components 161 cmd = ["docker-compose"] + cmdArgs 162 else: 163 cmdArgs = command + componentList 164 cmd = ["docker"] + cmdArgs 165 166 #print("cmd:", cmd) 167 output, error, returncode = \ 168 self._callCLI(cmd, expect_success=True, env=self.getEnv()) 169 170 # Don't rebuild if ps command 171 if command[0] !="ps" and command[0] !="config": 172 self.rebuildContainerData() 173 return output 174 175 def rebuildContainerData(self): 176 self.containerDataList = [] 177 for containerID in self.refreshContainerIDs(): 178 179 # get container metadata 180 container = json.loads(bdd_test_util.cli_call(["docker", "inspect", containerID], expect_success=True)[0])[0] 181 182 # container name 183 container_name = container['Name'][1:] 184 185 # container ip address (only if container is running) 186 container_ipaddress = None 187 if container['State']['Running']: 188 container_ipaddress = container['NetworkSettings']['IPAddress'] 189 if not container_ipaddress: 190 # ipaddress not found at the old location, try the new location 191 container_ipaddress = container['NetworkSettings']['Networks'].values()[0]['IPAddress'] 192 193 # container environment 194 container_env = container['Config']['Env'] 195 196 # container exposed ports 197 container_ports = container['NetworkSettings']['Ports'] 198 199 # container docker-compose service 200 container_compose_service = container['Config']['Labels']['com.docker.compose.service'] 201 202 self.containerDataList.append(peer_basic_impl.ContainerData(container_name, container_ipaddress, container_env, container_compose_service, container_ports)) 203 204 def decompose(self): 205 self.issueCommand(["unpause"]) 206 self.issueCommand(["down"]) 207 self.issueCommand(["kill"]) 208 self.issueCommand(["rm", "-f"]) 209 210 # Now remove associated chaincode containers if any 211 output, error, returncode = \ 212 bdd_test_util.cli_call(["docker"] + ["ps", "-qa", "--filter", "name={0}".format(self.projectName)], expect_success=True, env=self.getEnv()) 213 for containerId in output.splitlines(): 214 output, error, returncode = \ 215 bdd_test_util.cli_call(["docker"] + ["rm", "-f", containerId], expect_success=True, env=self.getEnv()) 216 217 # Remove the associated network 218 output, error, returncode = \ 219 bdd_test_util.cli_call(["docker"] + ["network", "ls", "-q", "--filter", "name={0}".format(self.projectName)], expect_success=True, env=self.getEnv()) 220 for networkId in output.splitlines(): 221 output, error, returncode = \ 222 bdd_test_util.cli_call(["docker"] + ["network", "rm", networkId], expect_success=True, env=self.getEnv()) 223 224 # Invoke callbacks 225 [callback.decomposing(self, self.context) for callback in Composition.GetCompositionCallbacksFromContext(self.context)]