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

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