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

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package externalbuilder_test
     8  
     9  import (
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"time"
    15  
    16  	. "github.com/onsi/ginkgo"
    17  	. "github.com/onsi/gomega"
    18  	"go.uber.org/zap"
    19  	"go.uber.org/zap/zapcore"
    20  
    21  	"github.com/hyperledger/fabric/common/flogging"
    22  	"github.com/hyperledger/fabric/core/comm"
    23  	"github.com/hyperledger/fabric/core/container/ccintf"
    24  	"github.com/hyperledger/fabric/core/container/externalbuilder"
    25  )
    26  
    27  var _ = Describe("Instance", func() {
    28  	var (
    29  		logger   *flogging.FabricLogger
    30  		instance *externalbuilder.Instance
    31  	)
    32  
    33  	BeforeEach(func() {
    34  		enc := zapcore.NewConsoleEncoder(zapcore.EncoderConfig{MessageKey: "msg"})
    35  		core := zapcore.NewCore(enc, zapcore.AddSync(GinkgoWriter), zap.NewAtomicLevel())
    36  		logger = flogging.NewFabricLogger(zap.New(core).Named("logger"))
    37  
    38  		instance = &externalbuilder.Instance{
    39  			PackageID: "test-ccid",
    40  			Builder: &externalbuilder.Builder{
    41  				Location: "testdata/goodbuilder",
    42  				Logger:   logger,
    43  			},
    44  		}
    45  	})
    46  
    47  	Describe("ChaincodeServerInfo", func() {
    48  		BeforeEach(func() {
    49  			var err error
    50  			instance.ReleaseDir, err = ioutil.TempDir("", "cc-conn-test")
    51  			Expect(err).NotTo(HaveOccurred())
    52  
    53  			err = os.MkdirAll(filepath.Join(instance.ReleaseDir, "chaincode", "server"), 0755)
    54  			Expect(err).NotTo(HaveOccurred())
    55  			// initialize with a well-formed, all fields set, connection.json file
    56  			ccdata := `{"address": "ccaddress:12345", "tls_required": true, "dial_timeout": "10s", "client_auth_required": true, "client_key": "fake-key", "client_cert": "fake-cert", "root_cert": "fake-root-cert"}`
    57  			err = ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"), []byte(ccdata), 0600)
    58  			Expect(err).NotTo(HaveOccurred())
    59  		})
    60  
    61  		AfterEach(func() {
    62  			os.RemoveAll(instance.ReleaseDir)
    63  		})
    64  
    65  		It("returns chaincode connection", func() {
    66  			ccinfo, err := instance.ChaincodeServerInfo()
    67  			Expect(err).NotTo(HaveOccurred())
    68  			Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{
    69  				Address: "ccaddress:12345",
    70  				ClientConfig: comm.ClientConfig{
    71  					SecOpts: comm.SecureOptions{
    72  						UseTLS:            true,
    73  						RequireClientCert: true,
    74  						Certificate:       []byte("fake-cert"),
    75  						Key:               []byte("fake-key"),
    76  						ServerRootCAs:     [][]byte{[]byte("fake-root-cert")},
    77  					},
    78  					KaOpts:  comm.DefaultKeepaliveOptions,
    79  					Timeout: 10 * time.Second,
    80  				},
    81  			}))
    82  		})
    83  
    84  		When("connection.json is not provided", func() {
    85  			BeforeEach(func() {
    86  				err := os.Remove(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"))
    87  				Expect(err).NotTo(HaveOccurred())
    88  			})
    89  
    90  			It("returns nil server info", func() {
    91  				ccinfo, err := instance.ChaincodeServerInfo()
    92  				Expect(err).NotTo(HaveOccurred())
    93  				Expect(ccinfo).To(BeNil())
    94  			})
    95  		})
    96  
    97  		When("chaincode info is badly formed", func() {
    98  			BeforeEach(func() {
    99  				ccdata := `{"badly formed chaincode"}`
   100  				err := ioutil.WriteFile(filepath.Join(instance.ChaincodeServerReleaseDir(), "connection.json"), []byte(ccdata), 0600)
   101  				Expect(err).NotTo(HaveOccurred())
   102  			})
   103  
   104  			It("returns a malformed chaincode error", func() {
   105  				_, err := instance.ChaincodeServerInfo()
   106  				Expect(err).To(MatchError(ContainSubstring("malformed chaincode info")))
   107  			})
   108  		})
   109  	})
   110  
   111  	Describe("ChaincodeServerUserData", func() {
   112  		var (
   113  			ccuserdata *externalbuilder.ChaincodeServerUserData
   114  			releaseDir string
   115  		)
   116  
   117  		BeforeEach(func() {
   118  			var err error
   119  			releaseDir, err = ioutil.TempDir("", "cc-conn-test")
   120  			Expect(err).NotTo(HaveOccurred())
   121  
   122  			err = os.MkdirAll(filepath.Join(releaseDir, "chaincode", "server"), 0755)
   123  			Expect(err).NotTo(HaveOccurred())
   124  
   125  			ccuserdata = &externalbuilder.ChaincodeServerUserData{
   126  				Address:            "ccaddress:12345",
   127  				DialTimeout:        externalbuilder.Duration{10 * time.Second},
   128  				TLSRequired:        true,
   129  				ClientAuthRequired: true,
   130  				ClientKey:          "fake-key",
   131  				ClientCert:         "fake-cert",
   132  				RootCert:           "fake-root-cert",
   133  			}
   134  		})
   135  
   136  		AfterEach(func() {
   137  			os.RemoveAll(releaseDir)
   138  		})
   139  
   140  		When("chaincode does not provide all info", func() {
   141  			Context("tls is not provided", func() {
   142  				It("returns TLS without client auth information", func() {
   143  					ccuserdata.TLSRequired = false
   144  
   145  					ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   146  					Expect(err).NotTo(HaveOccurred())
   147  					Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{
   148  						Address: "ccaddress:12345",
   149  						ClientConfig: comm.ClientConfig{
   150  							Timeout: 10 * time.Second,
   151  							KaOpts:  comm.DefaultKeepaliveOptions,
   152  						},
   153  					}))
   154  				})
   155  			})
   156  
   157  			Context("client auth is not provided", func() {
   158  				It("returns TLS without client auth information", func() {
   159  					ccuserdata.ClientAuthRequired = false
   160  
   161  					ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   162  					Expect(err).NotTo(HaveOccurred())
   163  					Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{
   164  						Address: "ccaddress:12345",
   165  						ClientConfig: comm.ClientConfig{
   166  							SecOpts: comm.SecureOptions{
   167  								UseTLS:        true,
   168  								ServerRootCAs: [][]byte{[]byte("fake-root-cert")},
   169  							},
   170  							KaOpts:  comm.DefaultKeepaliveOptions,
   171  							Timeout: 10 * time.Second,
   172  						},
   173  					}))
   174  				})
   175  			})
   176  
   177  			Context("dial timeout not provided", func() {
   178  				It("returns default dial timeout without dialtimeout", func() {
   179  					ccuserdata.DialTimeout = externalbuilder.Duration{}
   180  
   181  					ccinfo, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   182  					Expect(err).NotTo(HaveOccurred())
   183  					Expect(ccinfo).To(Equal(&ccintf.ChaincodeServerInfo{
   184  						Address: "ccaddress:12345",
   185  						ClientConfig: comm.ClientConfig{
   186  							SecOpts: comm.SecureOptions{
   187  								UseTLS:            true,
   188  								RequireClientCert: true,
   189  								Certificate:       []byte("fake-cert"),
   190  								Key:               []byte("fake-key"),
   191  								ServerRootCAs:     [][]byte{[]byte("fake-root-cert")},
   192  							},
   193  							KaOpts:  comm.DefaultKeepaliveOptions,
   194  							Timeout: 3 * time.Second,
   195  						},
   196  					}))
   197  				})
   198  			})
   199  
   200  			Context("address is not provided", func() {
   201  				It("returns missing address error", func() {
   202  					ccuserdata.Address = ""
   203  
   204  					_, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   205  					Expect(err).To(MatchError("chaincode address not provided"))
   206  				})
   207  			})
   208  
   209  			Context("key is not provided", func() {
   210  				It("returns missing key error", func() {
   211  					ccuserdata.ClientKey = ""
   212  
   213  					_, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   214  					Expect(err).To(MatchError("chaincode tls key not provided"))
   215  				})
   216  			})
   217  
   218  			Context("cert is not provided", func() {
   219  				It("returns missing key error", func() {
   220  					ccuserdata.ClientCert = ""
   221  
   222  					_, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   223  					Expect(err).To(MatchError("chaincode tls cert not provided"))
   224  				})
   225  			})
   226  
   227  			Context("root cert is not provided", func() {
   228  				It("returns missing root cert error", func() {
   229  					ccuserdata.RootCert = ""
   230  
   231  					_, err := ccuserdata.ChaincodeServerInfo(releaseDir)
   232  					Expect(err).To(MatchError("chaincode tls root cert not provided"))
   233  				})
   234  			})
   235  		})
   236  	})
   237  
   238  	Describe("Start", func() {
   239  		It("invokes the builder's run command and sets the run status", func() {
   240  			err := instance.Start(&ccintf.PeerConnection{
   241  				Address: "fake-peer-address",
   242  				TLSConfig: &ccintf.TLSConfig{
   243  					ClientCert: []byte("fake-client-cert"),
   244  					ClientKey:  []byte("fake-client-key"),
   245  					RootCert:   []byte("fake-root-cert"),
   246  				},
   247  			})
   248  			Expect(err).NotTo(HaveOccurred())
   249  			Expect(instance.Session).NotTo(BeNil())
   250  
   251  			errCh := make(chan error)
   252  			go func() { errCh <- instance.Session.Wait() }()
   253  			Eventually(errCh).Should(Receive(BeNil()))
   254  		})
   255  	})
   256  
   257  	Describe("Stop", func() {
   258  		It("terminates the process", func() {
   259  			cmd := exec.Command("sleep", "90")
   260  			sess, err := externalbuilder.Start(logger, cmd)
   261  			Expect(err).NotTo(HaveOccurred())
   262  			instance.Session = sess
   263  			instance.TermTimeout = time.Minute
   264  
   265  			errCh := make(chan error)
   266  			go func() { errCh <- instance.Session.Wait() }()
   267  			Consistently(errCh).ShouldNot(Receive())
   268  
   269  			err = instance.Stop()
   270  			Expect(err).ToNot(HaveOccurred())
   271  			Eventually(errCh).Should(Receive(MatchError("signal: terminated")))
   272  		})
   273  
   274  		Context("when the process doesn't respond to SIGTERM within TermTimeout", func() {
   275  			It("kills the process with malice", func() {
   276  				cmd := exec.Command("testdata/ignoreterm.sh")
   277  				sess, err := externalbuilder.Start(logger, cmd)
   278  				Expect(err).NotTo(HaveOccurred())
   279  
   280  				instance.Session = sess
   281  				instance.TermTimeout = time.Second
   282  
   283  				errCh := make(chan error)
   284  				go func() { errCh <- instance.Session.Wait() }()
   285  				Consistently(errCh).ShouldNot(Receive())
   286  
   287  				err = instance.Stop()
   288  				Expect(err).ToNot(HaveOccurred())
   289  				Eventually(errCh).Should(Receive(MatchError("signal: killed")))
   290  			})
   291  		})
   292  
   293  		Context("when the instance session has not been started", func() {
   294  			It("returns an error", func() {
   295  				instance.Session = nil
   296  				err := instance.Stop()
   297  				Expect(err).To(MatchError("instance has not been started"))
   298  			})
   299  		})
   300  	})
   301  
   302  	Describe("Wait", func() {
   303  		BeforeEach(func() {
   304  			err := instance.Start(&ccintf.PeerConnection{
   305  				Address: "fake-peer-address",
   306  				TLSConfig: &ccintf.TLSConfig{
   307  					ClientCert: []byte("fake-client-cert"),
   308  					ClientKey:  []byte("fake-client-key"),
   309  					RootCert:   []byte("fake-root-cert"),
   310  				},
   311  			})
   312  			Expect(err).NotTo(HaveOccurred())
   313  		})
   314  
   315  		It("returns the exit status of the run", func() {
   316  			code, err := instance.Wait()
   317  			Expect(err).NotTo(HaveOccurred())
   318  			Expect(code).To(Equal(0))
   319  		})
   320  
   321  		Context("when run exits with a non-zero status", func() {
   322  			BeforeEach(func() {
   323  				instance.Builder.Location = "testdata/failbuilder"
   324  				instance.Builder.Name = "failbuilder"
   325  				err := instance.Start(&ccintf.PeerConnection{
   326  					Address: "fake-peer-address",
   327  				})
   328  				Expect(err).NotTo(HaveOccurred())
   329  			})
   330  
   331  			It("returns the exit status of the run and accompanying error", func() {
   332  				code, err := instance.Wait()
   333  				Expect(err).To(MatchError("builder 'failbuilder' run failed: exit status 1"))
   334  				Expect(code).To(Equal(1))
   335  			})
   336  		})
   337  
   338  		Context("when the instance session has not been started", func() {
   339  			It("returns an error", func() {
   340  				instance.Session = nil
   341  				exitCode, err := instance.Wait()
   342  				Expect(err).To(MatchError("instance was not successfully started"))
   343  				Expect(exitCode).To(Equal(-1))
   344  			})
   345  		})
   346  	})
   347  })