github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/test/feature/steps/compose_util.py (about) 1 # Copyright IBM Corp. 2017 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 sys 18 import subprocess 19 import json 20 import uuid 21 22 23 class ContainerData: 24 def __init__(self, containerName, ipAddress, envFromInspect, composeService, ports): 25 self.containerName = containerName 26 self.ipAddress = ipAddress 27 self.envFromInspect = envFromInspect 28 self.composeService = composeService 29 self.ports = ports 30 31 def getEnv(self, key): 32 envValue = None 33 for val in self.envFromInspect: 34 if val.startswith(key): 35 envValue = val[len(key):] 36 break 37 if envValue == None: 38 raise Exception("ENV key not found ({0}) for container ({1})".format(key, self.containerName)) 39 return envValue 40 41 42 class Composition: 43 44 def __init__(self, context, composeFilesYaml, projectName = None, 45 force_recreate = True, components = [], startContainers=True): 46 if not projectName: 47 projectName = str(uuid.uuid1()).replace('-','') 48 self.projectName = projectName 49 self.context = context 50 self.containerDataList = [] 51 self.composeFilesYaml = composeFilesYaml 52 if startContainers: 53 self.up(force_recreate, components) 54 55 def collectServiceNames(self): 56 'First collect the services names.' 57 servicesList = [service for service in self.issueCommand(["config", "--services"]).splitlines() if "WARNING" not in service] 58 return servicesList 59 60 def up(self, force_recreate=True, components=[]): 61 command = ["up", "-d"] 62 if force_recreate: 63 command += ["--force-recreate"] 64 self.issueCommand(command + components) 65 66 def scale(self, serviceName, count=1): 67 command = ["scale", "%s=%d" %(serviceName, count)] 68 self.issueCommand(command) 69 70 def stop(self, components=[]): 71 command = ["stop"] 72 self.issueCommand(command, components) 73 74 def start(self, components=[]): 75 self.serviceNames = self.collectServiceNames() 76 command = ["start"] 77 self.issueCommand(command, components) 78 79 def docker_exec(self, command, components=[]): 80 results = {} 81 updatedCommand = " ".join(command) 82 for component in components: 83 execCommand = ["exec", component, updatedCommand] 84 results[component] = self.issueCommand(execCommand, []) 85 return results 86 87 def parseComposeFilesArg(self, composeFileArgs): 88 composeFileList = [] 89 for composeFile in composeFileArgs.split(): 90 if not os.path.isdir(composeFile): 91 composeFileList.append(composeFile) 92 else: 93 composeFileList.append(os.path.join(composeFile, 'docker-compose.yml')) 94 95 argSubList = [["-f", composeFile] for composeFile in composeFileList] 96 args = [arg for sublist in argSubList for arg in sublist] 97 return args 98 99 def getFileArgs(self): 100 return self.parseComposeFilesArg(self.composeFilesYaml) 101 102 def getEnvAdditions(self): 103 myEnv = {} 104 myEnv["COMPOSE_PROJECT_NAME"] = self.projectName 105 myEnv["CORE_PEER_NETWORKID"] = self.projectName 106 return myEnv 107 108 def getEnv(self): 109 myEnv = os.environ.copy() 110 for key,value in self.getEnvAdditions().items(): 111 myEnv[key] = value 112 return myEnv 113 114 def refreshContainerIDs(self): 115 containers = self.issueCommand(["ps", "-q"]).split() 116 return containers 117 118 def getContainerIP(self, container): 119 container_ipaddress = None 120 if container['State']['Running']: 121 container_ipaddress = container['NetworkSettings']['IPAddress'] 122 if not container_ipaddress: 123 # ipaddress not found at the old location, try the new location 124 container_ipaddress = container['NetworkSettings']['Networks'].values()[0]['IPAddress'] 125 return container_ipaddress 126 127 def getContainerFromName(self, containerName, containerList): 128 container = None 129 for container in containerList: 130 if containerName == container.containerName: 131 break 132 return container 133 134 def issueCommand(self, command, components=[]): 135 componentList = [] 136 useCompose = True 137 for component in components: 138 if '_' in component: 139 useCompose = False 140 componentList.append("%s_%s" % (self.projectName, component)) 141 else: 142 break 143 144 # If we need to perform an operation on a specific container, use 145 # docker not docker-compose 146 if useCompose and command[0] != "exec": 147 cmdArgs = self.getFileArgs()+ command + components 148 cmd = ["docker-compose"] + cmdArgs 149 elif command[0] == "exec": 150 cmdArgs = command + componentList 151 cmdList = ["docker"] + cmdArgs 152 cmd = [" ".join(cmdList)] 153 else: 154 cmdArgs = command + componentList 155 cmd = ["docker"] + cmdArgs 156 157 try: 158 if cmd[0].startswith("docker exec"): 159 process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.getEnv()) 160 output, _error = process.communicate() 161 if "Error: " in _error or "CRIT " in _error: 162 raise Exception(_error) 163 else: 164 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=self.getEnv()) 165 output, _error = process.communicate() 166 if _error: 167 raise Exception(_error) 168 except: 169 err = "Error occurred {0}: {1}".format(cmd, sys.exc_info()[1]) 170 output = err 171 172 # Don't rebuild if ps command 173 if command[0] !="ps" and command[0] !="config": 174 self.rebuildContainerData() 175 return str(output) 176 177 def rebuildContainerData(self): 178 self.containerDataList = [] 179 for containerID in self.refreshContainerIDs(): 180 # get container metadata 181 container = json.loads(str(subprocess.check_output(["docker", "inspect", containerID])))[0] 182 # container name 183 container_name = container['Name'][1:] 184 # container ip address (only if container is running) 185 container_ipaddress = self.getContainerIP(container) 186 # container environment 187 container_env = container['Config']['Env'] 188 # container exposed ports 189 container_ports = container['NetworkSettings']['Ports'] 190 # container docker-compose service 191 container_compose_service = container['Config']['Labels']['com.docker.compose.service'] 192 container_data = ContainerData(container_name, 193 container_ipaddress, 194 container_env, 195 container_compose_service, 196 container_ports) 197 self.containerDataList.append(container_data) 198 199 def decompose(self): 200 self.issueCommand(["unpause"]) 201 self.issueCommand(["down"]) 202 self.issueCommand(["kill"]) 203 self.issueCommand(["rm", "-f"]) 204 env = self.getEnv() 205 206 # Now remove associated chaincode containers if any 207 cmd = ["docker", "ps", "-qa", "--filter", "name={0}".format(self.projectName)] 208 output = str(subprocess.check_output(cmd, env=env)) 209 container_list = output.strip().split('\n') 210 for container in container_list: 211 if container != '': 212 subprocess.call(['docker', 'rm', '-f', container], env=env) 213 214 # Need to remove the chaincode images: docker rmi -f $(docker images | grep "example.com-" | awk '{print $3}') 215 cmd = ['docker images | grep ".example.com-" | awk \'{print $3}\' | xargs docker rmi'] 216 subprocess.call(cmd, shell=True, env=env)