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)