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