github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/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/hyperledger/fabric/common/ledger/testutil"
    37  	"github.com/hyperledger/fabric/common/util"
    38  	"github.com/hyperledger/fabric/core/chaincode/platforms"
    39  	"github.com/hyperledger/fabric/core/container/ccintf"
    40  	coreutil "github.com/hyperledger/fabric/core/testutil"
    41  	pb "github.com/hyperledger/fabric/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/hyperledger/fabric/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  }