github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/core/container/dockercontroller/dockercontroller_test.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package dockercontroller
     8  
     9  import (
    10  	"archive/tar"
    11  	"bytes"
    12  	"compress/gzip"
    13  	"context"
    14  	"encoding/hex"
    15  	"errors"
    16  	"fmt"
    17  	"io"
    18  	"io/ioutil"
    19  	"testing"
    20  	"time"
    21  
    22  	docker "github.com/fsouza/go-dockerclient"
    23  	pb "github.com/hyperledger/fabric-protos-go/peer"
    24  	"github.com/hyperledger/fabric/common/flogging/floggingtest"
    25  	"github.com/hyperledger/fabric/common/metrics/disabled"
    26  	"github.com/hyperledger/fabric/common/metrics/metricsfakes"
    27  	"github.com/hyperledger/fabric/common/util"
    28  	"github.com/hyperledger/fabric/core/chaincode/persistence"
    29  	"github.com/hyperledger/fabric/core/container/ccintf"
    30  	"github.com/hyperledger/fabric/core/container/dockercontroller/mock"
    31  	. "github.com/onsi/gomega"
    32  	"github.com/onsi/gomega/gbytes"
    33  	"github.com/stretchr/testify/assert"
    34  	"github.com/stretchr/testify/require"
    35  )
    36  
    37  // This test used to be part of an integration style test in core/container, moved to here
    38  func TestIntegrationPath(t *testing.T) {
    39  	client, err := docker.NewClientFromEnv()
    40  	assert.NoError(t, err)
    41  
    42  	fakePlatformBuilder := &mock.PlatformBuilder{}
    43  	fakePlatformBuilder.GenerateDockerBuildReturns(InMemBuilder{}.Build())
    44  
    45  	dc := DockerVM{
    46  		PeerID:          "",
    47  		NetworkID:       util.GenerateUUID(),
    48  		BuildMetrics:    NewBuildMetrics(&disabled.Provider{}),
    49  		Client:          client,
    50  		PlatformBuilder: fakePlatformBuilder,
    51  	}
    52  	ccid := "simple"
    53  
    54  	instance, err := dc.Build("simple", &persistence.ChaincodePackageMetadata{
    55  		Type: "type",
    56  		Path: "path",
    57  	}, bytes.NewBuffer([]byte("code-package")))
    58  	require.NoError(t, err)
    59  
    60  	assert.Equal(t, &ContainerInstance{
    61  		CCID:     "simple",
    62  		Type:     "TYPE",
    63  		DockerVM: &dc,
    64  	}, instance)
    65  
    66  	err = dc.Start(ccid, "GOLANG", &ccintf.PeerConnection{
    67  		Address: "peer-address",
    68  	})
    69  	require.NoError(t, err)
    70  
    71  	err = dc.Stop(ccid)
    72  	require.NoError(t, err)
    73  }
    74  
    75  var expectedNodeStartScript = `
    76  set -e
    77  if [ -x /chaincode/start.sh ]; then
    78  	/chaincode/start.sh --peer.address peer-address
    79  else
    80  	cd /usr/local/src
    81  	npm start -- --peer.address peer-address
    82  fi
    83  `
    84  
    85  func TestGetArgs(t *testing.T) {
    86  	tests := []struct {
    87  		name         string
    88  		ccType       pb.ChaincodeSpec_Type
    89  		expectedArgs []string
    90  		expectedErr  string
    91  	}{
    92  		{"golang-chaincode", pb.ChaincodeSpec_GOLANG, []string{"chaincode", "-peer.address=peer-address"}, ""},
    93  		{"java-chaincode", pb.ChaincodeSpec_JAVA, []string{"/root/chaincode-java/start", "--peerAddress", "peer-address"}, ""},
    94  		{"node-chaincode", pb.ChaincodeSpec_NODE, []string{"/bin/sh", "-c", expectedNodeStartScript}, ""},
    95  		{"unknown-chaincode", pb.ChaincodeSpec_Type(999), []string{}, "unknown chaincodeType: 999"},
    96  	}
    97  	for _, tc := range tests {
    98  		vm := &DockerVM{}
    99  
   100  		args, err := vm.GetArgs(tc.ccType.String(), "peer-address")
   101  		if tc.expectedErr != "" {
   102  			assert.EqualError(t, err, tc.expectedErr)
   103  			continue
   104  		}
   105  		assert.NoError(t, err)
   106  		assert.Equal(t, tc.expectedArgs, args)
   107  	}
   108  }
   109  
   110  func TestGetEnv(t *testing.T) {
   111  	vm := &DockerVM{
   112  		LoggingEnv: []string{"LOG_ENV=foo"},
   113  	}
   114  
   115  	t.Run("nil TLS config", func(t *testing.T) {
   116  		env := vm.GetEnv("test", nil)
   117  		assert.Equal(t, []string{"CORE_CHAINCODE_ID_NAME=test", "LOG_ENV=foo", "CORE_PEER_TLS_ENABLED=false"}, env)
   118  	})
   119  
   120  	t.Run("real TLS config", func(t *testing.T) {
   121  		env := vm.GetEnv("test", &ccintf.TLSConfig{
   122  			ClientKey:  []byte("key"),
   123  			ClientCert: []byte("cert"),
   124  			RootCert:   []byte("root"),
   125  		})
   126  		assert.Equal(t, []string{
   127  			"CORE_CHAINCODE_ID_NAME=test",
   128  			"LOG_ENV=foo",
   129  			"CORE_PEER_TLS_ENABLED=true",
   130  			"CORE_TLS_CLIENT_KEY_PATH=/etc/hyperledger/fabric/client.key",
   131  			"CORE_TLS_CLIENT_CERT_PATH=/etc/hyperledger/fabric/client.crt",
   132  			"CORE_TLS_CLIENT_KEY_FILE=/etc/hyperledger/fabric/client_pem.key",
   133  			"CORE_TLS_CLIENT_CERT_FILE=/etc/hyperledger/fabric/client_pem.crt",
   134  			"CORE_PEER_TLS_ROOTCERT_FILE=/etc/hyperledger/fabric/peer.crt",
   135  		}, env)
   136  	})
   137  }
   138  
   139  func Test_Start(t *testing.T) {
   140  	gt := NewGomegaWithT(t)
   141  	dockerClient := &mock.DockerClient{}
   142  	dvm := DockerVM{
   143  		BuildMetrics: NewBuildMetrics(&disabled.Provider{}),
   144  		Client:       dockerClient,
   145  	}
   146  
   147  	ccid := "simple:1.0"
   148  	peerConnection := &ccintf.PeerConnection{
   149  		Address: "peer-address",
   150  		TLSConfig: &ccintf.TLSConfig{
   151  			ClientKey:  []byte("key"),
   152  			ClientCert: []byte("cert"),
   153  			RootCert:   []byte("root"),
   154  		},
   155  	}
   156  
   157  	// case 1: dockerClient.CreateContainer returns error
   158  	testError1 := errors.New("junk1")
   159  	dockerClient.CreateContainerReturns(nil, testError1)
   160  	err := dvm.Start(ccid, "GOLANG", peerConnection)
   161  	gt.Expect(err).To(MatchError(testError1))
   162  	dockerClient.CreateContainerReturns(&docker.Container{}, nil)
   163  
   164  	// case 2: dockerClient.UploadToContainer returns error
   165  	testError2 := errors.New("junk2")
   166  	dockerClient.UploadToContainerReturns(testError2)
   167  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   168  	gt.Expect(err.Error()).To(ContainSubstring("junk2"))
   169  	dockerClient.UploadToContainerReturns(nil)
   170  
   171  	// case 3: start called and dockerClient.CreateContainer returns
   172  	// docker.noSuchImgErr and dockerClient.Start returns error
   173  	testError3 := errors.New("junk3")
   174  	dvm.AttachStdOut = true
   175  	dockerClient.CreateContainerReturns(nil, testError3)
   176  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   177  	gt.Expect(err).To(MatchError(testError3))
   178  	dockerClient.CreateContainerReturns(&docker.Container{}, nil)
   179  
   180  	// case 4: GetArgs returns error
   181  	err = dvm.Start(ccid, "FAKE_TYPE", peerConnection)
   182  	gt.Expect(err).To(MatchError("could not get args: unknown chaincodeType: FAKE_TYPE"))
   183  
   184  	// Success cases
   185  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   186  	gt.Expect(err).NotTo(HaveOccurred())
   187  
   188  	// dockerClient.StopContainer returns error
   189  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   190  	gt.Expect(err).NotTo(HaveOccurred())
   191  
   192  	// dockerClient.KillContainer returns error
   193  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   194  	gt.Expect(err).NotTo(HaveOccurred())
   195  
   196  	// dockerClient.RemoveContainer returns error
   197  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   198  	gt.Expect(err).NotTo(HaveOccurred())
   199  
   200  	err = dvm.Start(ccid, "GOLANG", peerConnection)
   201  	gt.Expect(err).NotTo(HaveOccurred())
   202  }
   203  
   204  func Test_streamOutput(t *testing.T) {
   205  	gt := NewGomegaWithT(t)
   206  
   207  	logger, recorder := floggingtest.NewTestLogger(t)
   208  	containerLogger, containerRecorder := floggingtest.NewTestLogger(t)
   209  
   210  	client := &mock.DockerClient{}
   211  	errCh := make(chan error, 1)
   212  	optsCh := make(chan docker.AttachToContainerOptions, 1)
   213  	client.AttachToContainerStub = func(opts docker.AttachToContainerOptions) error {
   214  		optsCh <- opts
   215  		return <-errCh
   216  	}
   217  
   218  	streamOutput(logger, client, "container-name", containerLogger)
   219  
   220  	var opts docker.AttachToContainerOptions
   221  	gt.Eventually(optsCh).Should(Receive(&opts))
   222  	gt.Eventually(opts.Success).Should(BeSent(struct{}{}))
   223  	gt.Eventually(opts.Success).Should(BeClosed())
   224  
   225  	fmt.Fprintf(opts.OutputStream, "message-one\n")
   226  	fmt.Fprintf(opts.OutputStream, "message-two") // does not get written
   227  	gt.Eventually(containerRecorder).Should(gbytes.Say("message-one"))
   228  	gt.Consistently(containerRecorder.Entries).Should(HaveLen(1))
   229  
   230  	close(errCh)
   231  	gt.Eventually(recorder).Should(gbytes.Say("Container container-name has closed its IO channel"))
   232  	gt.Consistently(recorder.Entries).Should(HaveLen(1))
   233  	gt.Consistently(containerRecorder.Entries).Should(HaveLen(1))
   234  }
   235  
   236  func Test_BuildMetric(t *testing.T) {
   237  	ccid := "simple:1.0"
   238  	client := &mock.DockerClient{}
   239  
   240  	tests := []struct {
   241  		desc           string
   242  		buildErr       bool
   243  		expectedLabels []string
   244  	}{
   245  		{desc: "success", buildErr: false, expectedLabels: []string{"chaincode", "simple:1.0", "success", "true"}},
   246  		{desc: "failure", buildErr: true, expectedLabels: []string{"chaincode", "simple:1.0", "success", "false"}},
   247  	}
   248  	for _, tt := range tests {
   249  		t.Run(tt.desc, func(t *testing.T) {
   250  			gt := NewGomegaWithT(t)
   251  			fakeChaincodeImageBuildDuration := &metricsfakes.Histogram{}
   252  			fakeChaincodeImageBuildDuration.WithReturns(fakeChaincodeImageBuildDuration)
   253  			dvm := DockerVM{
   254  				BuildMetrics: &BuildMetrics{
   255  					ChaincodeImageBuildDuration: fakeChaincodeImageBuildDuration,
   256  				},
   257  				Client: client,
   258  			}
   259  
   260  			if tt.buildErr {
   261  				client.BuildImageReturns(errors.New("Error building image"))
   262  			}
   263  			dvm.buildImage(ccid, &bytes.Buffer{})
   264  
   265  			gt.Expect(fakeChaincodeImageBuildDuration.WithCallCount()).To(Equal(1))
   266  			gt.Expect(fakeChaincodeImageBuildDuration.WithArgsForCall(0)).To(Equal(tt.expectedLabels))
   267  			gt.Expect(fakeChaincodeImageBuildDuration.ObserveArgsForCall(0)).NotTo(BeZero())
   268  			gt.Expect(fakeChaincodeImageBuildDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0))
   269  		})
   270  	}
   271  }
   272  
   273  func Test_Stop(t *testing.T) {
   274  	dvm := DockerVM{Client: &mock.DockerClient{}}
   275  	ccid := "simple"
   276  
   277  	// Success case
   278  	err := dvm.Stop(ccid)
   279  	assert.NoError(t, err)
   280  }
   281  
   282  func Test_Wait(t *testing.T) {
   283  	dvm := DockerVM{}
   284  
   285  	// happy path
   286  	client := &mock.DockerClient{}
   287  	dvm.Client = client
   288  
   289  	client.WaitContainerReturns(99, nil)
   290  	exitCode, err := dvm.Wait("the-name:the-version")
   291  	assert.NoError(t, err)
   292  	assert.Equal(t, 99, exitCode)
   293  	assert.Equal(t, "the-name-the-version", client.WaitContainerArgsForCall(0))
   294  
   295  	// wait fails
   296  	client.WaitContainerReturns(99, errors.New("no-wait-for-you"))
   297  	_, err = dvm.Wait("")
   298  	assert.EqualError(t, err, "no-wait-for-you")
   299  }
   300  
   301  func TestHealthCheck(t *testing.T) {
   302  	client := &mock.DockerClient{}
   303  	vm := &DockerVM{Client: client}
   304  
   305  	err := vm.HealthCheck(context.Background())
   306  	assert.NoError(t, err)
   307  
   308  	client.PingWithContextReturns(errors.New("Error pinging daemon"))
   309  	err = vm.HealthCheck(context.Background())
   310  	assert.Error(t, err)
   311  	assert.Contains(t, err.Error(), "Error pinging daemon")
   312  }
   313  
   314  type testCase struct {
   315  	name           string
   316  	vm             *DockerVM
   317  	ccid           string
   318  	expectedOutput string
   319  }
   320  
   321  func TestGetVMNameForDocker(t *testing.T) {
   322  	tc := []testCase{
   323  		{
   324  			name:           "mycc",
   325  			vm:             &DockerVM{NetworkID: "dev", PeerID: "peer0"},
   326  			ccid:           "mycc:1.0",
   327  			expectedOutput: fmt.Sprintf("%s-%s", "dev-peer0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("dev-peer0-mycc-1.0")))),
   328  		},
   329  		{
   330  			name:           "mycc-nonetworkid",
   331  			vm:             &DockerVM{PeerID: "peer1"},
   332  			ccid:           "mycc:1.0",
   333  			expectedOutput: fmt.Sprintf("%s-%s", "peer1-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("peer1-mycc-1.0")))),
   334  		},
   335  		{
   336  			name:           "myCC-UCids",
   337  			vm:             &DockerVM{NetworkID: "Dev", PeerID: "Peer0"},
   338  			ccid:           "myCC:1.0",
   339  			expectedOutput: fmt.Sprintf("%s-%s", "dev-peer0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("Dev-Peer0-myCC-1.0")))),
   340  		},
   341  		{
   342  			name:           "myCC-idsWithSpecialChars",
   343  			vm:             &DockerVM{NetworkID: "Dev$dev", PeerID: "Peer*0"},
   344  			ccid:           "myCC:1.0",
   345  			expectedOutput: fmt.Sprintf("%s-%s", "dev-dev-peer-0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("Dev$dev-Peer*0-myCC-1.0")))),
   346  		},
   347  		{
   348  			name:           "mycc-nopeerid",
   349  			vm:             &DockerVM{NetworkID: "dev"},
   350  			ccid:           "mycc:1.0",
   351  			expectedOutput: fmt.Sprintf("%s-%s", "dev-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("dev-mycc-1.0")))),
   352  		},
   353  		{
   354  			name:           "myCC-LCids",
   355  			vm:             &DockerVM{NetworkID: "dev", PeerID: "peer0"},
   356  			ccid:           "myCC:1.0",
   357  			expectedOutput: fmt.Sprintf("%s-%s", "dev-peer0-mycc-1.0", hex.EncodeToString(util.ComputeSHA256([]byte("dev-peer0-myCC-1.0")))),
   358  		},
   359  	}
   360  
   361  	for _, test := range tc {
   362  		name, err := test.vm.GetVMNameForDocker(test.ccid)
   363  		assert.Nil(t, err, "Expected nil error")
   364  		assert.Equal(t, test.expectedOutput, name, "Unexpected output for test case name: %s", test.name)
   365  	}
   366  
   367  }
   368  
   369  func TestGetVMName(t *testing.T) {
   370  	tc := []testCase{
   371  		{
   372  			name:           "myCC-preserveCase",
   373  			vm:             &DockerVM{NetworkID: "Dev", PeerID: "Peer0"},
   374  			ccid:           "myCC:1.0",
   375  			expectedOutput: fmt.Sprintf("%s", "Dev-Peer0-myCC-1.0"),
   376  		},
   377  	}
   378  
   379  	for _, test := range tc {
   380  		name := test.vm.GetVMName(test.ccid)
   381  		assert.Equal(t, test.expectedOutput, name, "Unexpected output for test case name: %s", test.name)
   382  	}
   383  
   384  }
   385  
   386  func Test_buildImage(t *testing.T) {
   387  	client := &mock.DockerClient{}
   388  	dvm := DockerVM{
   389  		BuildMetrics: NewBuildMetrics(&disabled.Provider{}),
   390  		Client:       client,
   391  		NetworkMode:  "network-mode",
   392  	}
   393  
   394  	err := dvm.buildImage("simple", &bytes.Buffer{})
   395  	assert.NoError(t, err)
   396  	assert.Equal(t, 1, client.BuildImageCallCount())
   397  
   398  	opts := client.BuildImageArgsForCall(0)
   399  	assert.Equal(t, "simple-a7a39b72f29718e653e73503210fbb597057b7a1c77d1fe321a1afcff041d4e1", opts.Name)
   400  	assert.False(t, opts.Pull)
   401  	assert.Equal(t, "network-mode", opts.NetworkMode)
   402  	assert.Equal(t, &bytes.Buffer{}, opts.InputStream)
   403  	assert.NotNil(t, opts.OutputStream)
   404  }
   405  
   406  func Test_buildImageFailure(t *testing.T) {
   407  	client := &mock.DockerClient{}
   408  	client.BuildImageReturns(errors.New("oh-bother-we-failed-badly"))
   409  	dvm := DockerVM{
   410  		BuildMetrics: NewBuildMetrics(&disabled.Provider{}),
   411  		Client:       client,
   412  		NetworkMode:  "network-mode",
   413  	}
   414  
   415  	err := dvm.buildImage("simple", &bytes.Buffer{})
   416  	assert.EqualError(t, err, "oh-bother-we-failed-badly")
   417  }
   418  
   419  func TestBuild(t *testing.T) {
   420  	buildMetrics := NewBuildMetrics(&disabled.Provider{})
   421  	md := &persistence.ChaincodePackageMetadata{
   422  		Type: "type",
   423  		Path: "path",
   424  	}
   425  
   426  	t.Run("when the image does not exist", func(t *testing.T) {
   427  		client := &mock.DockerClient{}
   428  		client.InspectImageReturns(nil, docker.ErrNoSuchImage)
   429  
   430  		fakePlatformBuilder := &mock.PlatformBuilder{}
   431  		fakePlatformBuilder.GenerateDockerBuildReturns(&bytes.Buffer{}, nil)
   432  
   433  		dvm := &DockerVM{Client: client, BuildMetrics: buildMetrics, PlatformBuilder: fakePlatformBuilder}
   434  		_, err := dvm.Build("chaincode-name:chaincode-version", md, bytes.NewBuffer([]byte("code-package")))
   435  		assert.NoError(t, err, "should have built successfully")
   436  
   437  		assert.Equal(t, 1, client.BuildImageCallCount())
   438  
   439  		require.Equal(t, 1, fakePlatformBuilder.GenerateDockerBuildCallCount())
   440  		ccType, path, codePackageStream := fakePlatformBuilder.GenerateDockerBuildArgsForCall(0)
   441  		assert.Equal(t, "TYPE", ccType)
   442  		assert.Equal(t, "path", path)
   443  		codePackage, err := ioutil.ReadAll(codePackageStream)
   444  		require.NoError(t, err)
   445  		assert.Equal(t, []byte("code-package"), codePackage)
   446  
   447  	})
   448  
   449  	t.Run("when inspecting the image fails", func(t *testing.T) {
   450  		client := &mock.DockerClient{}
   451  		client.InspectImageReturns(nil, errors.New("inspecting-image-fails"))
   452  
   453  		dvm := &DockerVM{Client: client, BuildMetrics: buildMetrics}
   454  		_, err := dvm.Build("chaincode-name:chaincode-version", md, bytes.NewBuffer([]byte("code-package")))
   455  		assert.EqualError(t, err, "docker image inspection failed: inspecting-image-fails")
   456  
   457  		assert.Equal(t, 0, client.BuildImageCallCount())
   458  	})
   459  
   460  	t.Run("when the image exists", func(t *testing.T) {
   461  		client := &mock.DockerClient{}
   462  
   463  		dvm := &DockerVM{Client: client, BuildMetrics: buildMetrics}
   464  		_, err := dvm.Build("chaincode-name:chaincode-version", md, bytes.NewBuffer([]byte("code-package")))
   465  		assert.NoError(t, err)
   466  
   467  		assert.Equal(t, 0, client.BuildImageCallCount())
   468  	})
   469  
   470  	t.Run("when the platform builder fails", func(t *testing.T) {
   471  		client := &mock.DockerClient{}
   472  		client.InspectImageReturns(nil, docker.ErrNoSuchImage)
   473  		client.BuildImageReturns(errors.New("no-build-for-you"))
   474  
   475  		fakePlatformBuilder := &mock.PlatformBuilder{}
   476  		fakePlatformBuilder.GenerateDockerBuildReturns(nil, errors.New("fake-builder-error"))
   477  
   478  		dvm := &DockerVM{Client: client, BuildMetrics: buildMetrics, PlatformBuilder: fakePlatformBuilder}
   479  		_, err := dvm.Build("chaincode-name:chaincode-version", md, bytes.NewBuffer([]byte("code-package")))
   480  		assert.Equal(t, 1, client.InspectImageCallCount())
   481  		assert.Equal(t, 1, fakePlatformBuilder.GenerateDockerBuildCallCount())
   482  		assert.Equal(t, 0, client.BuildImageCallCount())
   483  		assert.EqualError(t, err, "platform builder failed: fake-builder-error")
   484  	})
   485  
   486  	t.Run("when building the image fails", func(t *testing.T) {
   487  		client := &mock.DockerClient{}
   488  		client.InspectImageReturns(nil, docker.ErrNoSuchImage)
   489  		client.BuildImageReturns(errors.New("no-build-for-you"))
   490  
   491  		fakePlatformBuilder := &mock.PlatformBuilder{}
   492  
   493  		dvm := &DockerVM{Client: client, BuildMetrics: buildMetrics, PlatformBuilder: fakePlatformBuilder}
   494  		_, err := dvm.Build("chaincode-name:chaincode-version", md, bytes.NewBuffer([]byte("code-package")))
   495  		assert.Equal(t, 1, client.InspectImageCallCount())
   496  		assert.Equal(t, 1, client.BuildImageCallCount())
   497  		assert.EqualError(t, err, "docker image build failed: no-build-for-you")
   498  	})
   499  }
   500  
   501  type InMemBuilder struct{}
   502  
   503  func (imb InMemBuilder) Build() (io.Reader, error) {
   504  	buf := &bytes.Buffer{}
   505  	fmt.Fprintln(buf, "FROM busybox:latest")
   506  	fmt.Fprintln(buf, `RUN ln -s /bin/true /bin/chaincode`)
   507  	fmt.Fprintln(buf, `CMD ["tail", "-f", "/dev/null"]`)
   508  
   509  	startTime := time.Now()
   510  	inputbuf := bytes.NewBuffer(nil)
   511  	gw := gzip.NewWriter(inputbuf)
   512  	tr := tar.NewWriter(gw)
   513  	tr.WriteHeader(&tar.Header{
   514  		Name:       "Dockerfile",
   515  		Size:       int64(buf.Len()),
   516  		ModTime:    startTime,
   517  		AccessTime: startTime,
   518  		ChangeTime: startTime,
   519  	})
   520  	tr.Write(buf.Bytes())
   521  	tr.Close()
   522  	gw.Close()
   523  	return inputbuf, nil
   524  }
   525  
   526  type mockBuilder struct {
   527  	buildFunc func() (io.Reader, error)
   528  }
   529  
   530  func (m *mockBuilder) Build() (io.Reader, error) {
   531  	return m.buildFunc()
   532  }