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 }