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)]