diff --git a/core/container/controller.go b/core/container/controller.go index 9f771f459cc..045c99151a3 100644 --- a/core/container/controller.go +++ b/core/container/controller.go @@ -66,7 +66,7 @@ func (vmc *VMController) newVM(typ string) api.VM { switch typ { case DOCKER: - v = &dockercontroller.DockerVM{} + v = dockercontroller.NewDockerVM() case SYSTEM: v = &inproccontroller.InprocVM{} default: diff --git a/core/container/dockercontroller/dockercontroller.go b/core/container/dockercontroller/dockercontroller.go index 4a95c64a693..0c6c3a7b8f1 100644 --- a/core/container/dockercontroller/dockercontroller.go +++ b/core/container/dockercontroller/dockercontroller.go @@ -42,9 +42,49 @@ var ( hostConfig *docker.HostConfig ) +// getClient returns an instance that implements dockerClient interface +type getClient func() (dockerClient, error) + //DockerVM is a vm. It is identified by an image id type DockerVM struct { - id string + id string + getClientFnc getClient +} + +// dockerClient represents a docker client +type dockerClient interface { + // CreateContainer creates a docker container, returns an error in case of failure + CreateContainer(opts docker.CreateContainerOptions) (*docker.Container, error) + // StartContainer starts a docker container, returns an error in case of failure + StartContainer(id string, cfg *docker.HostConfig) error + // AttachToContainer attaches to a docker container, returns an error in case of + // failure + AttachToContainer(opts docker.AttachToContainerOptions) error + // BuildImage builds an image from a tarball's url or a Dockerfile in the input + // stream, returns an error in case of failure + BuildImage(opts docker.BuildImageOptions) error + // RemoveImageExtended removes a docker image by its name or ID, returns an + // error in case of failure + RemoveImageExtended(id string, opts docker.RemoveImageOptions) error + // StopContainer stops a docker container, killing it after the given timeout + // (in seconds). Returns an error in case of failure + StopContainer(id string, timeout uint) error + // KillContainer sends a signal to a docker container, returns an error in + // case of failure + KillContainer(opts docker.KillContainerOptions) error + // RemoveContainer removes a docker container, returns an error in case of failure + RemoveContainer(opts docker.RemoveContainerOptions) error +} + +// NewDockerVM returns a new DockerVM instance +func NewDockerVM() *DockerVM { + vm := DockerVM{} + vm.getClientFnc = getDockerClient + return &vm +} + +func getDockerClient() (dockerClient, error) { + return cutil.NewDockerClient() } func getDockerHostConfig() *docker.HostConfig { @@ -107,7 +147,9 @@ func getDockerHostConfig() *docker.HostConfig { return hostConfig } -func (vm *DockerVM) createContainer(ctxt context.Context, client *docker.Client, imageID string, containerID string, args []string, env []string, attachStdout bool) error { +func (vm *DockerVM) createContainer(ctxt context.Context, client dockerClient, + imageID string, containerID string, args []string, + env []string, attachStdout bool) error { config := docker.Config{Cmd: args, Image: imageID, Env: env, AttachStdout: attachStdout, AttachStderr: attachStdout} copts := docker.CreateContainerOptions{Name: containerID, Config: &config, HostConfig: getDockerHostConfig()} dockerLogger.Debugf("Create container: %s", containerID) @@ -119,7 +161,8 @@ func (vm *DockerVM) createContainer(ctxt context.Context, client *docker.Client, return nil } -func (vm *DockerVM) deployImage(client *docker.Client, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error { +func (vm *DockerVM) deployImage(client dockerClient, ccid ccintf.CCID, + args []string, env []string, reader io.Reader) error { id, err := vm.GetVMName(ccid) if err != nil { return err @@ -147,8 +190,10 @@ func (vm *DockerVM) deployImage(client *docker.Client, ccid ccintf.CCID, args [] //for docker inputbuf is tar reader ready for use by docker.Client //the stream from end client to peer could directly be this tar stream //talk to docker daemon using docker Client and build the image -func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, reader io.Reader) error { - client, err := cutil.NewDockerClient() +func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID, + args []string, env []string, reader io.Reader) error { + + client, err := vm.getClientFnc() switch err { case nil: if err = vm.deployImage(client, ccid, args, env, reader); err != nil { @@ -161,12 +206,14 @@ func (vm *DockerVM) Deploy(ctxt context.Context, ccid ccintf.CCID, args []string } //Start starts a container using a previously created docker image -func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, env []string, builder container.BuildSpecFactory) error { +func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, + args []string, env []string, builder container.BuildSpecFactory) error { imageID, err := vm.GetVMName(ccid) if err != nil { return err } - client, err := cutil.NewDockerClient() + + client, err := vm.getClientFnc() if err != nil { dockerLogger.Debugf("start - cannot create client %s", err) return err @@ -187,19 +234,19 @@ func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, if builder != nil { dockerLogger.Debugf("start-could not find image ...attempt to recreate image %s", err) - reader, err := builder() - if err != nil { - dockerLogger.Errorf("Error creating image builder: %s", err) + reader, err1 := builder() + if err1 != nil { + dockerLogger.Errorf("Error creating image builder: %s", err1) } - if err = vm.deployImage(client, ccid, args, env, reader); err != nil { - return err + if err1 = vm.deployImage(client, ccid, args, env, reader); err1 != nil { + return err1 } dockerLogger.Debug("start-recreated image successfully") - if err = vm.createContainer(ctxt, client, imageID, containerID, args, env, attachStdout); err != nil { - dockerLogger.Errorf("start-could not recreate container post recreate image: %s", err) - return err + if err1 = vm.createContainer(ctxt, client, imageID, containerID, args, env, attachStdout); err1 != nil { + dockerLogger.Errorf("start-could not recreate container post recreate image: %s", err1) + return err1 } } else { dockerLogger.Errorf("start-could not find image: %s", err) @@ -262,13 +309,13 @@ func (vm *DockerVM) Start(ctxt context.Context, ccid ccintf.CCID, args []string, for { // Loop forever dumping lines of text into the containerLogger // until the pipe is closed - line, err := is.ReadString('\n') - if err != nil { - switch err { + line, err2 := is.ReadString('\n') + if err2 != nil { + switch err2 { case io.EOF: dockerLogger.Infof("Container %s has closed its IO channel", containerID) default: - dockerLogger.Errorf("Error reading container output: %s", err) + dockerLogger.Errorf("Error reading container output: %s", err2) } return @@ -296,7 +343,8 @@ func (vm *DockerVM) Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, d if err != nil { return err } - client, err := cutil.NewDockerClient() + + client, err := vm.getClientFnc() if err != nil { dockerLogger.Debugf("stop - cannot create client %s", err) return err @@ -308,7 +356,8 @@ func (vm *DockerVM) Stop(ctxt context.Context, ccid ccintf.CCID, timeout uint, d return err } -func (vm *DockerVM) stopInternal(ctxt context.Context, client *docker.Client, id string, timeout uint, dontkill bool, dontremove bool) error { +func (vm *DockerVM) stopInternal(ctxt context.Context, client dockerClient, + id string, timeout uint, dontkill bool, dontremove bool) error { err := client.StopContainer(id, timeout) if err != nil { dockerLogger.Debugf("Stop container %s(%s)", id, err) @@ -340,7 +389,8 @@ func (vm *DockerVM) Destroy(ctxt context.Context, ccid ccintf.CCID, force bool, if err != nil { return err } - client, err := cutil.NewDockerClient() + + client, err := vm.getClientFnc() if err != nil { dockerLogger.Errorf("destroy-cannot create client %s", err) return err diff --git a/core/container/dockercontroller/dockercontroller_test.go b/core/container/dockercontroller/dockercontroller_test.go index c30d743a351..af8932375b3 100644 --- a/core/container/dockercontroller/dockercontroller_test.go +++ b/core/container/dockercontroller/dockercontroller_test.go @@ -17,15 +17,27 @@ limitations under the License. package dockercontroller import ( + "archive/tar" + "bytes" + "compress/gzip" + "context" + "errors" "fmt" + "io" "os" "testing" + "time" "github.com/fsouza/go-dockerclient" "github.com/spf13/viper" + "github.com/stretchr/testify/assert" "github.com/hyperledger/fabric/common/ledger/testutil" + "github.com/hyperledger/fabric/common/util" + "github.com/hyperledger/fabric/core/chaincode/platforms" + "github.com/hyperledger/fabric/core/container/ccintf" coreutil "github.com/hyperledger/fabric/core/testutil" + pb "github.com/hyperledger/fabric/protos/peer" ) func TestHostConfig(t *testing.T) { @@ -54,3 +66,241 @@ func TestGetDockerHostConfig(t *testing.T) { testutil.AssertEquals(t, hostConfig.Memory, int64(1024*1024*1024*2)) testutil.AssertEquals(t, hostConfig.CPUShares, int64(1024*1024*1024*2)) } + +func Test_Deploy(t *testing.T) { + dvm := DockerVM{} + ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} + //get the tarball for codechain + tarRdr := getCodeChainBytesInMem() + args := make([]string, 1) + env := make([]string, 1) + ctx := context.Background() + + // getMockClient returns error + getClientErr = true + dvm.getClientFnc = getMockClient + err := dvm.Deploy(ctx, ccid, args, env, tarRdr) + testerr(t, err, false) + getClientErr = false + + // Failure case: dockerClient.BuildImage returns error + buildErr = true + dvm.getClientFnc = getMockClient + err = dvm.Deploy(ctx, ccid, args, env, tarRdr) + testerr(t, err, false) + buildErr = false + + // Success case + err = dvm.Deploy(ctx, ccid, args, env, tarRdr) + testerr(t, err, true) +} + +func Test_Start(t *testing.T) { + dvm := DockerVM{} + ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} + args := make([]string, 1) + env := make([]string, 1) + ctx := context.Background() + + // Failure cases + // case 1: getMockClient returns error + dvm.getClientFnc = getMockClient + getClientErr = true + err := dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, false) + getClientErr = false + + // case 2: dockerClient.CreateContainer returns error + createErr = true + err = dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, false) + createErr = false + + // case 3: dockerClient.CreateContainer returns docker.noSuchImgErr + noSuchImgErr = true + err = dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, false) + + chaincodePath := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01" + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, + ChaincodeId: &pb.ChaincodeID{Name: "ex01", Path: chaincodePath}, + Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} + codePackage, err := platforms.GetDeploymentPayload(spec) + if err != nil { + t.Fatal() + } + cds := &pb.ChaincodeDeploymentSpec{ChaincodeSpec: spec, CodePackage: codePackage} + bldr := func() (io.Reader, error) { return platforms.GenerateDockerBuild(cds) } + + // case 4: start called with builder and dockerClient.CreateContainer returns + // docker.noSuchImgErr and dockerClient.Start returns error + viper.Set("vm.docker.attachStdout", true) + startErr = true + err = dvm.Start(ctx, ccid, args, env, bldr) + testerr(t, err, false) + startErr = false + + // Success cases + err = dvm.Start(ctx, ccid, args, env, bldr) + testerr(t, err, true) + noSuchImgErr = false + + // dockerClient.StopContainer returns error + stopErr = true + err = dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, true) + stopErr = false + + // dockerClient.KillContainer returns error + killErr = true + err = dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, true) + killErr = false + + // dockerClient.RemoveContainer returns error + removeErr = true + err = dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, true) + removeErr = false + + err = dvm.Start(ctx, ccid, args, env, nil) + testerr(t, err, true) +} + +func Test_Stop(t *testing.T) { + dvm := DockerVM{} + ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} + ctx := context.Background() + + // Failure case: getMockClient returns error + getClientErr = true + dvm.getClientFnc = getMockClient + err := dvm.Stop(ctx, ccid, 10, true, true) + testerr(t, err, false) + getClientErr = false + + // Success case + err = dvm.Stop(ctx, ccid, 10, true, true) + testerr(t, err, true) +} + +func Test_Destroy(t *testing.T) { + dvm := DockerVM{} + ccid := ccintf.CCID{ChaincodeSpec: &pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: "simple"}}} + ctx := context.Background() + + // Failure cases + // Case 1: getMockClient returns error + getClientErr = true + dvm.getClientFnc = getMockClient + err := dvm.Destroy(ctx, ccid, true, true) + testerr(t, err, false) + getClientErr = false + + // Case 2: dockerClient.RemoveImageExtended returns error + removeImgErr = true + err = dvm.Destroy(ctx, ccid, true, true) + testerr(t, err, false) + removeImgErr = false + + // Success case + err = dvm.Destroy(ctx, ccid, true, true) + testerr(t, err, true) +} + +func getCodeChainBytesInMem() io.Reader { + startTime := time.Now() + inputbuf := bytes.NewBuffer(nil) + gw := gzip.NewWriter(inputbuf) + tr := tar.NewWriter(gw) + dockerFileContents := []byte("FROM busybox:latest\n\nCMD echo hello") + dockerFileSize := int64(len([]byte(dockerFileContents))) + + tr.WriteHeader(&tar.Header{Name: "Dockerfile", Size: dockerFileSize, + ModTime: startTime, AccessTime: startTime, ChangeTime: startTime}) + tr.Write([]byte(dockerFileContents)) + tr.Close() + gw.Close() + return inputbuf +} + +func testerr(t *testing.T, err error, succ bool) { + if succ { + assert.NoError(t, err, "Expected success but got error") + } else { + assert.Error(t, err, "Expected failure but succeeded") + } +} + +func getMockClient() (dockerClient, error) { + if getClientErr { + return nil, errors.New("Failed to get client") + } + return &mockClient{noSuchImgErrReturned: false}, nil +} + +type mockClient struct { + noSuchImgErrReturned bool +} + +var getClientErr, createErr, noSuchImgErr, buildErr, removeImgErr, + startErr, stopErr, killErr, removeErr bool + +func (c *mockClient) CreateContainer(options docker.CreateContainerOptions) (*docker.Container, error) { + if createErr { + return nil, errors.New("Error creating the container") + } else if noSuchImgErr && !c.noSuchImgErrReturned { + c.noSuchImgErrReturned = true + return nil, docker.ErrNoSuchImage + } + return &docker.Container{}, nil +} + +func (c *mockClient) StartContainer(id string, cfg *docker.HostConfig) error { + if startErr { + return errors.New("Error starting the container") + } + return nil +} + +func (c *mockClient) AttachToContainer(opts docker.AttachToContainerOptions) error { + if opts.Success != nil { + opts.Success <- struct{}{} + } + return nil +} + +func (c *mockClient) BuildImage(opts docker.BuildImageOptions) error { + if buildErr { + return errors.New("Error building image") + } + return nil +} + +func (c *mockClient) RemoveImageExtended(id string, opts docker.RemoveImageOptions) error { + if removeImgErr { + return errors.New("Error removing extended image") + } + return nil +} + +func (c *mockClient) StopContainer(id string, timeout uint) error { + if stopErr { + return errors.New("Error stopping container") + } + return nil +} + +func (c *mockClient) KillContainer(opts docker.KillContainerOptions) error { + if killErr { + return errors.New("Error killing container") + } + return nil +} + +func (c *mockClient) RemoveContainer(opts docker.RemoveContainerOptions) error { + if removeErr { + return errors.New("Error removing container") + } + return nil +} diff --git a/core/container/util/dockerutil_test.go b/core/container/util/dockerutil_test.go index 059622fc0e6..b0fcfa53c2f 100644 --- a/core/container/util/dockerutil_test.go +++ b/core/container/util/dockerutil_test.go @@ -20,12 +20,28 @@ import ( "testing" "github.com/hyperledger/fabric/common/metadata" + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" ) func TestUtil_DockerfileTemplateParser(t *testing.T) { expected := "FROM foo:" + getArch() + "-" + metadata.Version actual := parseDockerfileTemplate("FROM foo:$(ARCH)-$(PROJECT_VERSION)") - if actual != expected { - t.Errorf("Error parsing Dockerfile Template. Expected \"%s\", got \"%s\"", expected, actual) - } + assert.Equal(t, expected, actual, "Error parsing Dockerfile Template. Expected \"%s\", got \"%s\"", + expected, actual) +} + +func TestUtil_GetDockerfileFromConfig(t *testing.T) { + expected := "FROM " + metadata.DockerNamespace + ":" + getArch() + "-" + metadata.Version + path := "dt" + viper.Set(path, "FROM $(DOCKER_NS):$(ARCH)-$(PROJECT_VERSION)") + actual := GetDockerfileFromConfig(path) + assert.Equal(t, expected, actual, "Error parsing Dockerfile Template. Expected \"%s\", got \"%s\"", + expected, actual) +} + +func TestUtil_GetDockertClient(t *testing.T) { + viper.Set("vm.endpoint", "unix:///var/run/docker.sock") + _, err := NewDockerClient() + assert.NoError(t, err, "Error getting docker client") } diff --git a/core/container/util/writer_test.go b/core/container/util/writer_test.go new file mode 100644 index 00000000000..65926c6588b --- /dev/null +++ b/core/container/util/writer_test.go @@ -0,0 +1,208 @@ +/* +Copyright London Stock Exchange 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_WriteFileToPackage(t *testing.T) { + buf := bytes.NewBuffer(nil) + gw := gzip.NewWriter(buf) + tw := tar.NewWriter(gw) + err := WriteFileToPackage("blah", "", tw) + assert.Error(t, err, "Expected error writing non existent file to package") + + // Create a file and write it to tar writer + filename := "test.txt" + filecontent := "hello" + filepath := os.TempDir() + filename + err = ioutil.WriteFile(filepath, bytes.NewBufferString(filecontent).Bytes(), 0600) + assert.NoError(t, err, "Error creating file %s", filepath) + defer os.Remove(filepath) + + err = WriteFileToPackage(filepath, filename, tw) + assert.NoError(t, err, "Error returned by WriteFileToPackage while writing existing file") + tw.Close() + gw.Close() + + // Read the file from the archive and check the name and file content + r := bytes.NewReader(buf.Bytes()) + gr, err1 := gzip.NewReader(r) + assert.NoError(t, err1, "Error creating a gzip reader") + tr := tar.NewReader(gr) + header, err2 := tr.Next() + assert.NoError(t, err2, "Error getting the file from the tar") + assert.Equal(t, filename, header.Name, + "Name of the file read from the archive is not same as the file added to the archive") + + b := make([]byte, 5) + _, err3 := tr.Read(b) + assert.NoError(t, err3, "Error reading file from the archive") + assert.Equal(t, filecontent, bytes.NewBuffer(b).String(), + "file content from the archive is not same as original file content") + + // tar writer is closed. Call WriteFileToPackage again, this should + // return an error + err = WriteFileToPackage(filepath, "", tw) + fmt.Println(err) + assert.Error(t, err, "Expected error writing using a closed writer") +} + +func Test_WriteStreamToPackage(t *testing.T) { + tarw := tar.NewWriter(bytes.NewBuffer(nil)) + input := bytes.NewReader([]byte("hello")) + + // Error case 1 + err := WriteStreamToPackage(nil, "/nonexistentpath", "", tarw) + assert.Error(t, err, "Expected error getting info of non existent file") + + // Error case 2 + err = WriteStreamToPackage(input, os.TempDir(), "", tarw) + assert.Error(t, err, "Expected error copying to the tar writer (tarw)") + + tarw.Close() + + // Error case 3 + err = WriteStreamToPackage(input, os.TempDir(), "", tarw) + assert.Error(t, err, "Expected error copying to closed tar writer (tarw)") + + // Success case + buf := bytes.NewBuffer(nil) + gw := gzip.NewWriter(buf) + tw := tar.NewWriter(gw) + + // Create a file and write it to tar writer + filename := "test.txt" + filecontent := "hello" + filepath := os.TempDir() + filename + err = ioutil.WriteFile(filepath, bytes.NewBufferString(filecontent).Bytes(), 0600) + assert.NoError(t, err, "Error creating file %s", filepath) + defer os.Remove(filepath) + + // Read the file into a stream + var b []byte + b, err = ioutil.ReadFile(filepath) + assert.NoError(t, err, "Error reading file %s", filepath) + is := bytes.NewReader(b) + + // Write contents of the file stream to tar writer + err = WriteStreamToPackage(is, filepath, filename, tw) + assert.NoError(t, err, "Error copying file to the tar writer (tw)") + + // Close the writers + tw.Close() + gw.Close() + + // Read the file from the archive and check the name and file content + br := bytes.NewReader(buf.Bytes()) + gr, err1 := gzip.NewReader(br) + assert.NoError(t, err1, "Error creating a gzip reader") + tr := tar.NewReader(gr) + header, err2 := tr.Next() + assert.NoError(t, err2, "Error getting the file from the tar") + assert.Equal(t, filename, header.Name, + "Name of the file read from the archive is not same as the file added to the archive") + + b1 := make([]byte, 5) + _, err3 := tr.Read(b1) + assert.NoError(t, err3, "Error reading file from the archive") + assert.Equal(t, filecontent, bytes.NewBuffer(b1).String(), + "file content from the archive is not same as original file content") +} + +func Test_WriteFolderToPackage(t *testing.T) { + buf := bytes.NewBuffer(nil) + gw := gzip.NewWriter(buf) + tw := tar.NewWriter(gw) + + // Success case 1: with include and exclude file types and without exclude dir + gopath := os.Getenv("GOPATH") + gopath = filepath.SplitList(gopath)[0] + srcPath := filepath.Join(gopath, "src", + "github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample") + filePath := "src/src/main/java/example/SimpleSample.java" + includeFileTypes := map[string]bool{ + ".java": true, + } + excludeFileTypes := map[string]bool{ + ".xml": true, + } + + err := WriteFolderToTarPackage(tw, srcPath, "", + includeFileTypes, excludeFileTypes) + assert.NoError(t, err, "Error writing folder to package") + + tw.Close() + gw.Close() + + // Read the file from the archive and check the name + br := bytes.NewReader(buf.Bytes()) + gr, err1 := gzip.NewReader(br) + assert.NoError(t, err1, "Error creating a gzip reader") + tr := tar.NewReader(gr) + header, err2 := tr.Next() + assert.NoError(t, err2, "Error getting the file from the tar") + assert.Equal(t, filePath, header.Name, + "Name of the file read from the archive is not same as the file added to the archive") + + // Success case 2: with exclude dir and no include file types + srcPath = filepath.Join(gopath, "src", + "github.com/hyperledger/fabric/examples/chaincode/java") + tarw := tar.NewWriter(bytes.NewBuffer(nil)) + defer tarw.Close() + err = WriteFolderToTarPackage(tarw, srcPath, "SimpleSample", + nil, excludeFileTypes) + assert.NoError(t, err, "Error writing folder to package") +} + +func Test_WriteJavaProjectToPackage(t *testing.T) { + inputbuf := bytes.NewBuffer(nil) + gw := gzip.NewWriter(inputbuf) + tw := tar.NewWriter(gw) + + gopath := os.Getenv("GOPATH") + gopath = filepath.SplitList(gopath)[0] + pkgDir := filepath.Join(gopath, "src", + "github.com/hyperledger/fabric/examples/chaincode/java") + err := WriteJavaProjectToPackage(tw, pkgDir) + assert.NoError(t, err, "Error writing java project to package") + + // Close the tar writer and call WriteFileToPackage again, this should + // return an error + tw.Close() + gw.Close() + err = WriteJavaProjectToPackage(tw, pkgDir) + assert.Error(t, err, "WriteJavaProjectToPackage was called with closed writer, should have failed") +} + +func Test_WriteBytesToPackage(t *testing.T) { + inputbuf := bytes.NewBuffer(nil) + tw := tar.NewWriter(inputbuf) + defer tw.Close() + err := WriteBytesToPackage("foo", []byte("blah"), tw) + assert.NoError(t, err, "Error writing bytes to package") +} diff --git a/core/container/vm_test.go b/core/container/vm_test.go index b2b482e366e..0efac547518 100644 --- a/core/container/vm_test.go +++ b/core/container/vm_test.go @@ -23,6 +23,7 @@ import ( "github.com/hyperledger/fabric/common/util" pb "github.com/hyperledger/fabric/protos/peer" + "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -41,10 +42,7 @@ func TestVM_ListImages(t *testing.T) { t.Logf("Error getting VM: %s", err) } err = vm.ListImages(context.TODO()) - if err != nil { - t.Fail() - t.Logf("Error listing images: %s", err) - } + assert.NoError(t, err, "Error listing images") } func TestVM_BuildImage_ChaincodeLocal(t *testing.T) { @@ -56,11 +54,11 @@ func TestVM_BuildImage_ChaincodeLocal(t *testing.T) { } // Build the spec chaincodePath := "github.com/hyperledger/fabric/examples/chaincode/go/chaincode_example01" - spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: "ex01", Path: chaincodePath}, Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} - if err := vm.BuildChaincodeContainer(spec); err != nil { - t.Fail() - t.Log(err) - } + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, + ChaincodeId: &pb.ChaincodeID{Name: "ex01", Path: chaincodePath}, + Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} + err = vm.BuildChaincodeContainer(spec) + assert.NoError(t, err) } func TestVM_BuildImage_ChaincodeRemote(t *testing.T) { @@ -73,11 +71,24 @@ func TestVM_BuildImage_ChaincodeRemote(t *testing.T) { } // Build the spec chaincodePath := "https://github.com/prjayach/chaincode_examples/chaincode_example02" - spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeId: &pb.ChaincodeID{Name: "ex02", Path: chaincodePath}, Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} - if err := vm.BuildChaincodeContainer(spec); err != nil { - t.Fail() - t.Log(err) - } + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, + ChaincodeId: &pb.ChaincodeID{Name: "ex02", Path: chaincodePath}, + Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} + err = vm.BuildChaincodeContainer(spec) + assert.NoError(t, err) +} + +func TestVM_GetChaincodePackageBytes(t *testing.T) { + _, err := GetChaincodePackageBytes(nil) + assert.Error(t, err, + "GetChaincodePackageBytes did not return error when chaincode spec is nil") + + spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, + ChaincodeId: nil, + Input: &pb.ChaincodeInput{Args: util.ToChaincodeArgs("f")}} + _, err = GetChaincodePackageBytes(spec) + assert.Error(t, err, + "GetChaincodePackageBytes did not return error when chaincode ID is nil") } func TestVM_Chaincode_Compile(t *testing.T) {