github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/core/container/dockercontroller/dockercontroller_test.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 dockercontroller 18 19 import ( 20 "archive/tar" 21 "bytes" 22 "compress/gzip" 23 "context" 24 "encoding/hex" 25 "errors" 26 "fmt" 27 "io" 28 "os" 29 "testing" 30 "time" 31 32 "github.com/fsouza/go-dockerclient" 33 "github.com/spf13/viper" 34 "github.com/stretchr/testify/assert" 35 36 "github.com/inklabsfoundation/inkchain/common/ledger/testutil" 37 "github.com/inklabsfoundation/inkchain/common/util" 38 "github.com/inklabsfoundation/inkchain/core/chaincode/platforms" 39 "github.com/inklabsfoundation/inkchain/core/container/ccintf" 40 coreutil "github.com/inklabsfoundation/inkchain/core/testutil" 41 pb "github.com/inklabsfoundation/inkchain/protos/peer" 42 ) 43 44 func TestHostConfig(t *testing.T) { 45 coreutil.SetupTestConfig() 46 var hostConfig = new(docker.HostConfig) 47 err := viper.UnmarshalKey("vm.docker.hostConfig", hostConfig) 48 if err != nil { 49 t.Fatalf("Load docker HostConfig wrong, error: %s", err.Error()) 50 } 51 testutil.AssertNotEquals(t, hostConfig.LogConfig, nil) 52 testutil.AssertEquals(t, hostConfig.LogConfig.Type, "json-file") 53 testutil.AssertEquals(t, hostConfig.LogConfig.Config["max-size"], "50m") 54 testutil.AssertEquals(t, hostConfig.LogConfig.Config["max-file"], "5") 55 } 56 57 func TestGetDockerHostConfig(t *testing.T) { 58 os.Setenv("CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE", "overlay") 59 os.Setenv("CORE_VM_DOCKER_HOSTCONFIG_CPUSHARES", fmt.Sprint(1024*1024*1024*2)) 60 coreutil.SetupTestConfig() 61 hostConfig := getDockerHostConfig() 62 testutil.AssertNotNil(t, hostConfig) 63 testutil.AssertEquals(t, hostConfig.NetworkMode, "overlay") 64 testutil.AssertEquals(t, hostConfig.LogConfig.Type, "json-file") 65 testutil.AssertEquals(t, hostConfig.LogConfig.Config["max-size"], "50m") 66 testutil.AssertEquals(t, hostConfig.LogConfig.Config["max-file"], "5") 67 testutil.AssertEquals(t, hostConfig.Memory, int64(1024*1024*1024*2)) 68 testutil.AssertEquals(t, hostConfig.CPUShares, int64(1024*1024*1024*2)) 69 } 70 71 func Test_Deploy(t *testing.T) { 72 dvm := DockerVM{} 73 ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} 74 //get the tarball for codechain 75 tarRdr := getCodeChainBytesInMem() 76 args := make([]string, 1) 77 env := make([]string, 1) 78 ctx := context.Background() 79 80 // getMockClient returns error 81 getClientErr = true 82 dvm.getClientFnc = getMockClient 83 err := dvm.Deploy(ctx, ccid, args, env, tarRdr) 84 testerr(t, err, false) 85 getClientErr = false 86 87 // Failure case: dockerClient.BuildImage returns error 88 buildErr = true 89 dvm.getClientFnc = getMockClient 90 err = dvm.Deploy(ctx, ccid, args, env, tarRdr) 91 testerr(t, err, false) 92 buildErr = false 93 94 // Success case 95 err = dvm.Deploy(ctx, ccid, args, env, tarRdr) 96 testerr(t, err, true) 97 } 98 99 func Test_Start(t *testing.T) { 100 dvm := DockerVM{} 101 ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} 102 args := make([]string, 1) 103 env := make([]string, 1) 104 ctx := context.Background() 105 106 // Failure cases 107 // case 1: getMockClient returns error 108 dvm.getClientFnc = getMockClient 109 getClientErr = true 110 err := dvm.Start(ctx, ccid, args, env, nil, nil) 111 testerr(t, err, false) 112 getClientErr = false 113 114 // case 2: dockerClient.CreateContainer returns error 115 createErr = true 116 err = dvm.Start(ctx, ccid, args, env, nil, nil) 117 testerr(t, err, false) 118 createErr = false 119 120 // case 3: dockerClient.CreateContainer returns docker.noSuchImgErr 121 noSuchImgErr = true 122 err = dvm.Start(ctx, ccid, args, env, nil, nil) 123 testerr(t, err, false) 124 125 chaincodePath := "github.com/inklabsfoundation/inkchain/examples/chaincode/go/chaincode_example01" 126 spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, 127 ChaincodeId: &pb.ChaincodeID{Name: "ex01", Path: chaincodePath}, 128 Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} 129 codePackage, err := platforms.GetDeploymentPayload(spec) 130 if err != nil { 131 t.Fatal() 132 } 133 cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackage} 134 bldr := func() (io.Reader, error) { return platforms.GenerateDockerBuild(cds) } 135 136 // case 4: start called with builder and dockerClient.CreateContainer returns 137 // docker.noSuchImgErr and dockerClient.Start returns error 138 viper.Set("vm.docker.attachStdout", true) 139 startErr = true 140 err = dvm.Start(ctx, ccid, args, env, bldr, nil) 141 testerr(t, err, false) 142 startErr = false 143 144 // Success cases 145 err = dvm.Start(ctx, ccid, args, env, bldr, nil) 146 testerr(t, err, true) 147 noSuchImgErr = false 148 149 // dockerClient.StopContainer returns error 150 stopErr = true 151 err = dvm.Start(ctx, ccid, args, env, nil, nil) 152 testerr(t, err, true) 153 stopErr = false 154 155 // dockerClient.KillContainer returns error 156 killErr = true 157 err = dvm.Start(ctx, ccid, args, env, nil, nil) 158 testerr(t, err, true) 159 killErr = false 160 161 // dockerClient.RemoveContainer returns error 162 removeErr = true 163 err = dvm.Start(ctx, ccid, args, env, nil, nil) 164 testerr(t, err, true) 165 removeErr = false 166 167 err = dvm.Start(ctx, ccid, args, env, nil, nil) 168 testerr(t, err, true) 169 170 //test preLaunchFunc works correctly 171 preLaunchStr := "notset" 172 preLaunchFunc := func() error { 173 preLaunchStr = "set" 174 return nil 175 } 176 177 err = dvm.Start(ctx, ccid, args, env, nil, preLaunchFunc) 178 testerr(t, err, true) 179 assert.Equal(t, preLaunchStr, "set") 180 181 preLaunchFunc = func() error { 182 return fmt.Errorf("testing error path") 183 } 184 185 err = dvm.Start(ctx, ccid, args, env, nil, preLaunchFunc) 186 testerr(t, err, false) 187 } 188 189 func Test_Stop(t *testing.T) { 190 dvm := DockerVM{} 191 ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} 192 ctx := context.Background() 193 194 // Failure case: getMockClient returns error 195 getClientErr = true 196 dvm.getClientFnc = getMockClient 197 err := dvm.Stop(ctx, ccid, 10, true, true) 198 testerr(t, err, false) 199 getClientErr = false 200 201 // Success case 202 err = dvm.Stop(ctx, ccid, 10, true, true) 203 testerr(t, err, true) 204 } 205 206 func Test_Destroy(t *testing.T) { 207 dvm := DockerVM{} 208 ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} 209 ctx := context.Background() 210 211 // Failure cases 212 // Case 1: getMockClient returns error 213 getClientErr = true 214 dvm.getClientFnc = getMockClient 215 err := dvm.Destroy(ctx, ccid, true, true) 216 testerr(t, err, false) 217 getClientErr = false 218 219 // Case 2: dockerClient.RemoveImageExtended returns error 220 removeImgErr = true 221 err = dvm.Destroy(ctx, ccid, true, true) 222 testerr(t, err, false) 223 removeImgErr = false 224 225 // Success case 226 err = dvm.Destroy(ctx, ccid, true, true) 227 testerr(t, err, true) 228 } 229 230 type testCase struct { 231 name string 232 ccid ccintf.CCID 233 formatFunc func(string) (string, error) 234 expectedOutput string 235 } 236 237 func TestGetVMName(t *testing.T) { 238 dvm := DockerVM{} 239 var tc []testCase 240 241 tc = append(tc, 242 testCase{"mycc", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "mycc"}}, NetworkID: "dev", PeerID: "peer0", Version: "1.0"}, formatImageName, fmt.Sprintf("%s-%s", "dev-peer0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("dev-peer0-mycc-1.0"))))}, 243 testCase{"mycc-nonetworkid", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "mycc"}}, PeerID: "peer1", Version: "1.0"}, formatImageName, fmt.Sprintf("%s-%s", "peer1-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("peer1-mycc-1.0"))))}, 244 testCase{"myCC", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "myCC"}}, NetworkID: "Dev", PeerID: "Peer0", Version: "1.0"}, formatImageName, fmt.Sprintf("%s-%s", "dev-peer0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("Dev-Peer0-myCC-1.0"))))}, 245 testCase{"mycc-nopeerid", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "mycc"}}, NetworkID: "dev", Version: "1.0"}, formatImageName, fmt.Sprintf("%s-%s", "dev-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("dev-mycc-1.0"))))}, 246 testCase{"myCC", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "myCC"}}, NetworkID: "dev", PeerID: "peer0", Version: "1.0"}, formatImageName, fmt.Sprintf("%s-%s", "dev-peer0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("dev-peer0-myCC-1.0"))))}, 247 testCase{"myCC-preserveCase", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "myCC"}}, NetworkID: "Dev", PeerID: "Peer0", Version: "1.0"}, nil, fmt.Sprintf("%s", "Dev-Peer0-myCC-1.0")}, 248 testCase{"invalidCharsFormatFunction", ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "myCC"}}, NetworkID: "Dev", PeerID: "Peer0", Version: "1.0"}, formatInvalidChars, fmt.Sprintf("%s", "inv-lid-character--")}) 249 250 for _, test := range tc { 251 name, err := dvm.GetVMName(test.ccid, test.formatFunc) 252 assert.Nil(t, err, "Expected nil error") 253 assert.Equal(t, test.expectedOutput, name, "Unexpected output for test case name: %s", test.name) 254 } 255 256 } 257 258 func TestFormatImageName_invalidChars(t *testing.T) { 259 _, err := formatImageName("invalid*chars") 260 assert.NotNil(t, err, "Expected error") 261 } 262 263 func getCodeChainBytesInMem() io.Reader { 264 startTime := time.Now() 265 inputbuf := bytes.NewBuffer(nil) 266 gw := gzip.NewWriter(inputbuf) 267 tr := tar.NewWriter(gw) 268 dockerFileContents := []byte("FROM busybox:latest\n\nCMD echo hello") 269 dockerFileSize := int64(len([]byte(dockerFileContents))) 270 271 tr.WriteHeader(&tar.Header{Name: "Dockerfile", Size: dockerFileSize, 272 ModTime: startTime, AccessTime: startTime, ChangeTime: startTime}) 273 tr.Write([]byte(dockerFileContents)) 274 tr.Close() 275 gw.Close() 276 return inputbuf 277 } 278 279 func testerr(t *testing.T, err error, succ bool) { 280 if succ { 281 assert.NoError(t, err, "Expected success but got error") 282 } else { 283 assert.Error(t, err, "Expected failure but succeeded") 284 } 285 } 286 287 func getMockClient() (dockerClient, error) { 288 if getClientErr { 289 return nil, errors.New("Failed to get client") 290 } 291 return &mockClient{noSuchImgErrReturned: false}, nil 292 } 293 294 type mockClient struct { 295 noSuchImgErrReturned bool 296 } 297 298 var getClientErr, createErr, noSuchImgErr, buildErr, removeImgErr, 299 startErr, stopErr, killErr, removeErr bool 300 301 func (c *mockClient) CreateContainer(options docker.CreateContainerOptions) (*docker.Container, error) { 302 if createErr { 303 return nil, errors.New("Error creating the container") 304 } else if noSuchImgErr && !c.noSuchImgErrReturned { 305 c.noSuchImgErrReturned = true 306 return nil, docker.ErrNoSuchImage 307 } 308 return &docker.Container{}, nil 309 } 310 311 func (c *mockClient) StartContainer(id string, cfg *docker.HostConfig) error { 312 if startErr { 313 return errors.New("Error starting the container") 314 } 315 return nil 316 } 317 318 func (c *mockClient) AttachToContainer(opts docker.AttachToContainerOptions) error { 319 if opts.Success != nil { 320 opts.Success <- struct{}{} 321 } 322 return nil 323 } 324 325 func (c *mockClient) BuildImage(opts docker.BuildImageOptions) error { 326 if buildErr { 327 return errors.New("Error building image") 328 } 329 return nil 330 } 331 332 func (c *mockClient) RemoveImageExtended(id string, opts docker.RemoveImageOptions) error { 333 if removeImgErr { 334 return errors.New("Error removing extended image") 335 } 336 return nil 337 } 338 339 func (c *mockClient) StopContainer(id string, timeout uint) error { 340 if stopErr { 341 return errors.New("Error stopping container") 342 } 343 return nil 344 } 345 346 func (c *mockClient) KillContainer(opts docker.KillContainerOptions) error { 347 if killErr { 348 return errors.New("Error killing container") 349 } 350 return nil 351 } 352 353 func (c *mockClient) RemoveContainer(opts docker.RemoveContainerOptions) error { 354 if removeErr { 355 return errors.New("Error removing container") 356 } 357 return nil 358 } 359 360 func formatInvalidChars(name string) (string, error) { 361 return "inv@lid*character$/", nil 362 }