github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/bddtests/compose.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package bddtests
    18  
    19  import (
    20  	"fmt"
    21  	"os/exec"
    22  	"strings"
    23  
    24  	"github.com/fsouza/go-dockerclient"
    25  )
    26  
    27  const dockerComposeCommand = "docker-compose"
    28  
    29  // Composition represents a docker-compose execution and management
    30  type Composition struct {
    31  	endpoint      string
    32  	dockerClient  *docker.Client
    33  	apiContainers []docker.APIContainers
    34  
    35  	composeFilesYaml string
    36  	projectName      string
    37  	dockerHelper     DockerHelper
    38  }
    39  
    40  // NewComposition create a new Composition specifying the project name (for isolation) and the compose files.
    41  func NewComposition(projectName string, composeFilesYaml string) (composition *Composition, err error) {
    42  	errRetFunc := func() error {
    43  		return fmt.Errorf("Error creating new composition '%s' using compose yaml '%s':  %s", projectName, composeFilesYaml, err)
    44  	}
    45  
    46  	endpoint := "unix:///var/run/docker.sock"
    47  	composition = &Composition{composeFilesYaml: composeFilesYaml, projectName: projectName}
    48  	if composition.dockerClient, err = docker.NewClient(endpoint); err != nil {
    49  		return nil, errRetFunc()
    50  	}
    51  	if _, err = composition.issueCommand([]string{"up", "--force-recreate", "-d"}); err != nil {
    52  		return nil, errRetFunc()
    53  	}
    54  	if composition.dockerHelper, err = NewDockerCmdlineHelper(); err != nil {
    55  		return nil, errRetFunc()
    56  	}
    57  	// Now parse the current system
    58  	return composition, nil
    59  }
    60  
    61  func parseComposeFilesArg(composeFileArgs string) []string {
    62  	var args []string
    63  	for _, f := range strings.Fields(composeFileArgs) {
    64  		args = append(args, []string{"-f", f}...)
    65  	}
    66  	return args
    67  }
    68  
    69  func (c *Composition) getFileArgs() []string {
    70  	return parseComposeFilesArg(c.composeFilesYaml)
    71  }
    72  
    73  // GetContainerIDs returns the container IDs for the composition (NOTE: does NOT include those defined outside composition, eg. chaincode containers)
    74  func (c *Composition) GetContainerIDs() (containerIDs []string, err error) {
    75  	var cmdOutput string
    76  	if cmdOutput, err = c.issueCommand([]string{"ps", "-q"}); err != nil {
    77  		return nil, fmt.Errorf("Error getting container IDs for project '%s':  %s", c.projectName, err)
    78  	}
    79  	containerIDs = splitDockerCommandResults(cmdOutput)
    80  	return containerIDs, err
    81  }
    82  
    83  func (c *Composition) getEnv() []string {
    84  	return []string{"COMPOSE_PROJECT_NAME=" + c.projectName, "CORE_PEER_NETWORKID=" + c.projectName}
    85  }
    86  
    87  func (c *Composition) refreshContainerList() (err error) {
    88  	var allAPIContainers []docker.APIContainers
    89  	var thisProjectsContainers []docker.APIContainers
    90  	if thisProjectsContainers, err = c.dockerClient.ListContainers(docker.ListContainersOptions{All: true, Filters: map[string][]string{"name": {c.projectName}}}); err != nil {
    91  		return fmt.Errorf("Error refreshing container list for project '%s':  %s", c.projectName, err)
    92  	}
    93  	//if allApiContainers, err = c.dockerClient.ListContainers(docker.ListContainersOptions{All: true}); err != nil {
    94  	//	return fmt.Errorf("Error refreshing container list for project '%s':  %s", c.projectName, err)
    95  	//}
    96  	for _, apiContainer := range allAPIContainers {
    97  		if composeService, ok := apiContainer.Labels["com.docker.compose.service"]; ok == true {
    98  			fmt.Println(fmt.Sprintf("Container name:  %s, composeService: %s, IPAddress: %s", apiContainer.Names[0], composeService, apiContainer.Networks.Networks["bridge"].IPAddress))
    99  		}
   100  	}
   101  	c.apiContainers = thisProjectsContainers
   102  	return err
   103  }
   104  
   105  func (c *Composition) issueCommand(args []string) (_ string, err error) {
   106  	errRetFunc := func() error {
   107  		return fmt.Errorf("Error issuing command to docker-compose with args '%s':  %s", args, err)
   108  	}
   109  	var cmdArgs []string
   110  	cmdArgs = append(cmdArgs, c.getFileArgs()...)
   111  	cmdArgs = append(cmdArgs, args...)
   112  	var cmdOut []byte
   113  	cmd := exec.Command(dockerComposeCommand, cmdArgs...)
   114  	cmd.Env = append(cmd.Env, c.getEnv()...)
   115  	if cmdOut, err = cmd.CombinedOutput(); err != nil {
   116  		return string(cmdOut), errRetFunc()
   117  	}
   118  
   119  	// Reparse Container list
   120  	if err = c.refreshContainerList(); err != nil {
   121  		return "", errRetFunc()
   122  	}
   123  	return string(cmdOut), err
   124  }
   125  
   126  // Decompose decompose the composition.  Will also remove any containers with the same projectName prefix (eg. chaincode containers)
   127  func (c *Composition) Decompose() (output string, err error) {
   128  	//var containers []string
   129  	output, err = c.issueCommand([]string{"stop"})
   130  	output, err = c.issueCommand([]string{"rm", "-f"})
   131  	// Now remove associated chaincode containers if any
   132  	c.dockerHelper.RemoveContainersWithNamePrefix(c.projectName)
   133  	return output, err
   134  }
   135  
   136  // parseComposition parses the current docker-compose project from ps command
   137  func (c *Composition) parseComposition() (err error) {
   138  	//c.issueCommand()
   139  	return nil
   140  }
   141  
   142  // GetAPIContainerForComposeService return the docker.APIContainers with the supplied composeService name.
   143  func (c *Composition) GetAPIContainerForComposeService(composeService string) (apiContainer *docker.APIContainers, err error) {
   144  	for _, apiContainer := range c.apiContainers {
   145  		if currComposeService, ok := apiContainer.Labels["com.docker.compose.service"]; ok == true {
   146  			if currComposeService == composeService {
   147  				return &apiContainer, nil
   148  			}
   149  		}
   150  	}
   151  	return nil, fmt.Errorf("Could not find container with compose service '%s'", composeService)
   152  }
   153  
   154  // GetIPAddressForComposeService returns the IPAddress of the container with the supplied composeService name.
   155  func (c *Composition) GetIPAddressForComposeService(composeService string) (ipAddress string, err error) {
   156  	errRetFunc := func() error {
   157  		return fmt.Errorf("Error getting IPAddress for compose service '%s':  %s", composeService, err)
   158  	}
   159  	var apiContainer *docker.APIContainers
   160  	if apiContainer, err = c.GetAPIContainerForComposeService(composeService); err != nil {
   161  		return "", errRetFunc()
   162  	}
   163  	// Now get the IPAddress
   164  	return apiContainer.Networks.Networks["bridge"].IPAddress, nil
   165  }