Skip to content

Commit

Permalink
[FAB-14856] Replace getClientFunc with actual Client
Browse files Browse the repository at this point in the history
Replaced the DockerVM getClient function with an actual
Client

Change-Id: Ibb76db8373c28b0f6d50b79b4402e8b531613a3c
Signed-off-by: Brett Logan <Brett.T.Logan@ibm.com>
  • Loading branch information
Brett Logan authored and sykesm committed Apr 3, 2019
1 parent f690e17 commit 1c4f43b
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 123 deletions.
6 changes: 6 additions & 0 deletions core/chaincode/exectransaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"testing"
"time"

"github.com/fsouza/go-dockerclient"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/bccsp/factory"
"github.com/hyperledger/fabric/common/channelconfig"
Expand Down Expand Up @@ -156,10 +157,15 @@ func initPeer(chainIDs ...string) (*cm.Lifecycle, net.Listener, *ChaincodeSuppor
Version: "0",
PackageID: persistence.PackageID("tmap:0"),
}, nil)
client, err := docker.NewClientFromEnv()
if err != nil {
return nil, nil, nil, nil, err
}
provider := &dockercontroller.Provider{
PeerID: "",
NetworkID: "",
BuildMetrics: dockercontroller.NewBuildMetrics(&disabled.Provider{}),
Client: client,
}
chaincodeSupport := NewChaincodeSupport(
config,
Expand Down
80 changes: 21 additions & 59 deletions core/container/dockercontroller/dockercontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
"github.com/fsouza/go-dockerclient"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/common/util"
"github.com/hyperledger/fabric/core/config"
"github.com/hyperledger/fabric/core/container"
"github.com/hyperledger/fabric/core/container/ccintf"
cutil "github.com/hyperledger/fabric/core/container/util"
Expand All @@ -42,12 +41,9 @@ var (
imageRegExp = regexp.MustCompile("^[a-z0-9]+(([._-][a-z0-9]+)+)?$")
)

// 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 {
getClientFnc getClient
Client dockerClient
PeerID string
NetworkID string
BuildMetrics *BuildMetrics
Expand Down Expand Up @@ -92,31 +88,19 @@ type Provider struct {
PeerID string
NetworkID string
BuildMetrics *BuildMetrics
Client dockerClient
}

// NewVM creates a new DockerVM instance
func (p *Provider) NewVM() container.VM {
return &DockerVM{
PeerID: p.PeerID,
NetworkID: p.NetworkID,
getClientFnc: getDockerClient,
Client: p.Client,
BuildMetrics: p.BuildMetrics,
}
}

func getDockerClient() (dockerClient, error) {
endpoint := viper.GetString("vm.endpoint")
tlsEnabled := viper.GetBool("vm.docker.tls.enabled")
if tlsEnabled {
cert := config.GetPath("vm.docker.tls.cert.file")
key := config.GetPath("vm.docker.tls.key.file")
ca := config.GetPath("vm.docker.tls.ca.file")
return docker.NewTLSClient(endpoint, cert, key, ca)
} else {
return docker.NewClient(endpoint)
}
}

func getDockerHostConfig() *docker.HostConfig {
if hostConfig != nil {
return hostConfig
Expand Down Expand Up @@ -166,10 +150,10 @@ func getDockerHostConfig() *docker.HostConfig {
}
}

func (vm *DockerVM) createContainer(client dockerClient, imageID, containerID string, args, env []string, attachStdout bool) error {
func (vm *DockerVM) createContainer(imageID, containerID string, args, env []string, attachStdout bool) error {
logger := dockerLogger.With("imageID", imageID, "containerID", containerID)
logger.Debugw("create container")
_, err := client.CreateContainer(docker.CreateContainerOptions{
_, err := vm.Client.CreateContainer(docker.CreateContainerOptions{
Name: containerID,
Config: &docker.Config{
Cmd: args,
Expand All @@ -187,7 +171,7 @@ func (vm *DockerVM) createContainer(client dockerClient, imageID, containerID st
return nil
}

func (vm *DockerVM) deployImage(client dockerClient, ccid ccintf.CCID, reader io.Reader) error {
func (vm *DockerVM) deployImage(ccid ccintf.CCID, reader io.Reader) error {
id, err := vm.GetVMNameForDocker(ccid)
if err != nil {
return err
Expand All @@ -202,7 +186,7 @@ func (vm *DockerVM) deployImage(client dockerClient, ccid ccintf.CCID, reader io
}

startTime := time.Now()
err = client.BuildImage(opts)
err = vm.Client.BuildImage(opts)

vm.BuildMetrics.ChaincodeImageBuildDuration.With(
"chaincode", ccid.String(),
Expand Down Expand Up @@ -230,27 +214,21 @@ func (vm *DockerVM) Start(ccid ccintf.CCID, args, env []string, filesToUpload ma
containerName := vm.GetVMName(ccid)
logger := dockerLogger.With("imageName", imageName, "containerName", containerName)

client, err := vm.getClientFnc()
if err != nil {
logger.Debugf("failed to get docker client", "error", err)
return err
}
vm.stopInternal(containerName, 0, false, false)

vm.stopInternal(client, containerName, 0, false, false)

err = vm.createContainer(client, imageName, containerName, args, env, attachStdout)
err = vm.createContainer(imageName, containerName, args, env, attachStdout)
if err == docker.ErrNoSuchImage {
reader, err := builder.Build()
if err != nil {
return errors.Wrapf(err, "failed to generate Dockerfile to build %s", containerName)
}

err = vm.deployImage(client, ccid, reader)
err = vm.deployImage(ccid, reader)
if err != nil {
return err
}

err = vm.createContainer(client, imageName, containerName, args, env, attachStdout)
err = vm.createContainer(imageName, containerName, args, env, attachStdout)
if err != nil {
logger.Errorf("failed to create container: %s", err)
return err
Expand All @@ -263,7 +241,7 @@ func (vm *DockerVM) Start(ccid ccintf.CCID, args, env []string, filesToUpload ma
// stream stdout and stderr to chaincode logger
if attachStdout {
containerLogger := flogging.MustGetLogger("peer.chaincode." + containerName)
streamOutput(dockerLogger, client, containerName, containerLogger)
streamOutput(dockerLogger, vm.Client, containerName, containerLogger)
}

// upload specified files to the container before starting it
Expand All @@ -286,7 +264,7 @@ func (vm *DockerVM) Start(ccid ccintf.CCID, args, env []string, filesToUpload ma

gw.Close()

err := client.UploadToContainer(containerName, docker.UploadToContainerOptions{
err := vm.Client.UploadToContainer(containerName, docker.UploadToContainerOptions{
InputStream: bytes.NewReader(payload.Bytes()),
Path: "/",
NoOverwriteDirNonDir: false,
Expand All @@ -297,7 +275,7 @@ func (vm *DockerVM) Start(ccid ccintf.CCID, args, env []string, filesToUpload ma
}

// start container with HostConfig was deprecated since v1.10 and removed in v1.2
err = client.StartContainer(containerName, nil)
err = vm.Client.StartContainer(containerName, nil)
if err != nil {
dockerLogger.Errorf("start-could not start container: %s", err)
return err
Expand Down Expand Up @@ -369,26 +347,14 @@ func streamOutput(logger *flogging.FabricLogger, client dockerClient, containerN

// Stop stops a running chaincode
func (vm *DockerVM) Stop(ccid ccintf.CCID, timeout uint, dontkill bool, dontremove bool) error {
client, err := vm.getClientFnc()
if err != nil {
dockerLogger.Debugf("stop - cannot create client %s", err)
return err
}
id := vm.ccidToContainerID(ccid)

return vm.stopInternal(client, id, timeout, dontkill, dontremove)
return vm.stopInternal(id, timeout, dontkill, dontremove)
}

// Wait blocks until the container stops and returns the exit code of the container.
func (vm *DockerVM) Wait(ccid ccintf.CCID) (int, error) {
client, err := vm.getClientFnc()
if err != nil {
dockerLogger.Debugf("stop - cannot create client %s", err)
return 0, err
}
id := vm.ccidToContainerID(ccid)

return client.WaitContainer(id)
return vm.Client.WaitContainer(id)
}

func (vm *DockerVM) ccidToContainerID(ccid ccintf.CCID) string {
Expand All @@ -398,32 +364,28 @@ func (vm *DockerVM) ccidToContainerID(ccid ccintf.CCID) string {
// HealthCheck checks if the DockerVM is able to communicate with the Docker
// daemon.
func (vm *DockerVM) HealthCheck(ctx context.Context) error {
client, err := vm.getClientFnc()
if err != nil {
return errors.Wrap(err, "failed to connect to Docker daemon")
}
if err := client.PingWithContext(ctx); err != nil {
if err := vm.Client.PingWithContext(ctx); err != nil {
return errors.Wrap(err, "failed to ping to Docker daemon")
}
return nil
}

func (vm *DockerVM) stopInternal(client dockerClient, id string, timeout uint, dontkill, dontremove bool) error {
func (vm *DockerVM) stopInternal(id string, timeout uint, dontkill, dontremove bool) error {
logger := dockerLogger.With("id", id)

logger.Debugw("stopping container")
err := client.StopContainer(id, timeout)
err := vm.Client.StopContainer(id, timeout)
dockerLogger.Debugw("stop container result", "error", err)

if !dontkill {
logger.Debugw("killing container")
err = client.KillContainer(docker.KillContainerOptions{ID: id})
err = vm.Client.KillContainer(docker.KillContainerOptions{ID: id})
logger.Debugw("kill container result", "error", err)
}

if !dontremove {
logger.Debugw("removing container")
err = client.RemoveContainer(docker.RemoveContainerOptions{ID: id, Force: true})
err = vm.Client.RemoveContainer(docker.RemoveContainerOptions{ID: id, Force: true})
logger.Debugw("remove container result", "error", err)
}

Expand Down
61 changes: 19 additions & 42 deletions core/container/dockercontroller/dockercontroller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,18 @@ import (
// This test used to be part of an integration style test in core/container, moved to here
func TestIntegrationPath(t *testing.T) {
coreutil.SetupTestConfig()
client, err := docker.NewClientFromEnv()
assert.NoError(t, err)
provider := Provider{
PeerID: "",
NetworkID: util.GenerateUUID(),
BuildMetrics: NewBuildMetrics(&disabled.Provider{}),
Client: client,
}
dc := provider.NewVM()
ccid := ccintf.CCID("simple")

err := dc.Start(ccid, nil, nil, nil, InMemBuilder{})
err = dc.Start(ccid, nil, nil, nil, InMemBuilder{})
require.NoError(t, err)

// Stop, killing, and deleting
Expand Down Expand Up @@ -90,6 +93,7 @@ func Test_Start(t *testing.T) {
gt := NewGomegaWithT(t)
dvm := DockerVM{
BuildMetrics: NewBuildMetrics(&disabled.Provider{}),
Client: &mockClient{},
}
ccid := ccintf.CCID("simple:1.0")
args := make([]string, 1)
Expand All @@ -98,27 +102,19 @@ func Test_Start(t *testing.T) {
"hello": []byte("world"),
}

// Failure cases
// case 1: getMockClient returns error
dvm.getClientFnc = getMockClient
getClientErr = true
err := dvm.Start(ccid, args, env, files, nil)
gt.Expect(err).To(HaveOccurred())
getClientErr = false

// case 2: dockerClient.CreateContainer returns error
// case 1: dockerClient.CreateContainer returns error
createErr = true
err = dvm.Start(ccid, args, env, files, nil)
err := dvm.Start(ccid, args, env, files, nil)
gt.Expect(err).To(HaveOccurred())
createErr = false

// case 3: dockerClient.UploadToContainer returns error
// case 2: dockerClient.UploadToContainer returns error
uploadErr = true
err = dvm.Start(ccid, args, env, files, nil)
gt.Expect(err).To(HaveOccurred())
uploadErr = false

// case 4: dockerClient.StartContainer returns docker.noSuchImgErr, BuildImage fails
// case 3: dockerClient.StartContainer returns docker.noSuchImgErr, BuildImage fails
noSuchImgErr = true
buildErr = true
err = dvm.Start(ccid, args, env, files, &mockBuilder{buildFunc: func() (io.Reader, error) { return &bytes.Buffer{}, nil }})
Expand Down Expand Up @@ -148,7 +144,7 @@ func Test_Start(t *testing.T) {
},
}

// case 5: start called and dockerClient.CreateContainer returns
// case 4: start called and dockerClient.CreateContainer returns
// docker.noSuchImgErr and dockerClient.Start returns error
viper.Set("vm.docker.attachStdout", true)
startErr = true
Expand Down Expand Up @@ -236,10 +232,11 @@ func Test_BuildMetric(t *testing.T) {
BuildMetrics: &BuildMetrics{
ChaincodeImageBuildDuration: fakeChaincodeImageBuildDuration,
},
Client: client,
}

buildErr = tt.buildErr
dvm.deployImage(client, ccid, &bytes.Buffer{})
dvm.deployImage(ccid, &bytes.Buffer{})

gt.Expect(fakeChaincodeImageBuildDuration.WithCallCount()).To(Equal(1))
gt.Expect(fakeChaincodeImageBuildDuration.WithArgsForCall(0)).To(Equal(tt.expectedLabels))
Expand All @@ -252,34 +249,20 @@ func Test_BuildMetric(t *testing.T) {
}

func Test_Stop(t *testing.T) {
dvm := DockerVM{}
dvm := DockerVM{Client: &mockClient{}}
ccid := ccintf.CCID("simple")

// Failure case: getMockClient returns error
getClientErr = true
dvm.getClientFnc = getMockClient
err := dvm.Stop(ccid, 10, true, true)
assert.Error(t, err)
getClientErr = false

// Success case
err = dvm.Stop(ccid, 10, true, true)
err := dvm.Stop(ccid, 10, true, true)
assert.NoError(t, err)
}

func Test_Wait(t *testing.T) {
dvm := DockerVM{}

// failure to get a client
dvm.getClientFnc = func() (dockerClient, error) {
return nil, errors.New("gorilla-goo")
}
_, err := dvm.Wait(ccintf.CCID(""))
assert.EqualError(t, err, "gorilla-goo")

// happy path
client := &mockClient{}
dvm.getClientFnc = func() (dockerClient, error) { return client, nil }
dvm.Client = client

client.exitCode = 99
exitCode, err := dvm.Wait(ccintf.CCID("the-name:the-version"))
Expand All @@ -296,20 +279,14 @@ func Test_Wait(t *testing.T) {
func Test_HealthCheck(t *testing.T) {
dvm := DockerVM{}

dvm.getClientFnc = func() (dockerClient, error) {
client := &mockClient{
pingErr: false,
}
return client, nil
dvm.Client = &mockClient{
pingErr: false,
}
err := dvm.HealthCheck(context.Background())
assert.NoError(t, err)

dvm.getClientFnc = func() (dockerClient, error) {
client := &mockClient{
pingErr: true,
}
return client, nil
dvm.Client = &mockClient{
pingErr: true,
}
err = dvm.HealthCheck(context.Background())
assert.Error(t, err)
Expand Down
Loading

0 comments on commit 1c4f43b

Please sign in to comment.