github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/container/dockercontroller/dockercontroller_test.go (about)

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