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