github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/core/chaincode/runtime_launcher_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package chaincode_test
     8  
     9  import (
    10  	"time"
    11  
    12  	"github.com/hechain20/hechain/common/metrics/metricsfakes"
    13  	"github.com/hechain20/hechain/core/chaincode"
    14  	"github.com/hechain20/hechain/core/chaincode/accesscontrol"
    15  	"github.com/hechain20/hechain/core/chaincode/extcc"
    16  	extccmock "github.com/hechain20/hechain/core/chaincode/extcc/mock"
    17  	"github.com/hechain20/hechain/core/chaincode/fake"
    18  	"github.com/hechain20/hechain/core/chaincode/mock"
    19  	"github.com/hechain20/hechain/core/container/ccintf"
    20  	. "github.com/onsi/ginkgo"
    21  	. "github.com/onsi/gomega"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  var _ = Describe("RuntimeLauncher", func() {
    26  	var (
    27  		fakeRuntime        *mock.Runtime
    28  		fakeRegistry       *fake.LaunchRegistry
    29  		launchState        *chaincode.LaunchState
    30  		fakeLaunchDuration *metricsfakes.Histogram
    31  		fakeLaunchFailures *metricsfakes.Counter
    32  		fakeLaunchTimeouts *metricsfakes.Counter
    33  		fakeCertGenerator  *mock.CertGenerator
    34  		exitedCh           chan int
    35  		extCCConnExited    chan struct{}
    36  		fakeConnHandler    *mock.ConnectionHandler
    37  		fakeStreamHandler  *extccmock.StreamHandler
    38  
    39  		runtimeLauncher *chaincode.RuntimeLauncher
    40  	)
    41  
    42  	BeforeEach(func() {
    43  		launchState = chaincode.NewLaunchState()
    44  		fakeRegistry = &fake.LaunchRegistry{}
    45  		fakeRegistry.LaunchingReturns(launchState, false)
    46  
    47  		fakeRuntime = &mock.Runtime{}
    48  		fakeRuntime.StartStub = func(string, *ccintf.PeerConnection) error {
    49  			launchState.Notify(nil)
    50  			return nil
    51  		}
    52  		exitedCh = make(chan int)
    53  		waitExitCh := exitedCh // shadow to avoid race
    54  		fakeRuntime.WaitStub = func(string) (int, error) {
    55  			return <-waitExitCh, nil
    56  		}
    57  
    58  		fakeConnHandler = &mock.ConnectionHandler{}
    59  		fakeStreamHandler = &extccmock.StreamHandler{}
    60  
    61  		extCCConnExited = make(chan struct{})
    62  		connExited := extCCConnExited // shadow to avoid race
    63  		fakeConnHandler.StreamStub = func(string, *ccintf.ChaincodeServerInfo, extcc.StreamHandler) error {
    64  			launchState.Notify(nil)
    65  			<-connExited
    66  			return nil
    67  		}
    68  
    69  		fakeLaunchDuration = &metricsfakes.Histogram{}
    70  		fakeLaunchDuration.WithReturns(fakeLaunchDuration)
    71  		fakeLaunchFailures = &metricsfakes.Counter{}
    72  		fakeLaunchFailures.WithReturns(fakeLaunchFailures)
    73  		fakeLaunchTimeouts = &metricsfakes.Counter{}
    74  		fakeLaunchTimeouts.WithReturns(fakeLaunchTimeouts)
    75  
    76  		launchMetrics := &chaincode.LaunchMetrics{
    77  			LaunchDuration: fakeLaunchDuration,
    78  			LaunchFailures: fakeLaunchFailures,
    79  			LaunchTimeouts: fakeLaunchTimeouts,
    80  		}
    81  		fakeCertGenerator = &mock.CertGenerator{}
    82  		fakeCertGenerator.GenerateReturns(&accesscontrol.CertAndPrivKeyPair{Cert: []byte("cert"), Key: []byte("key")}, nil)
    83  		runtimeLauncher = &chaincode.RuntimeLauncher{
    84  			Runtime:           fakeRuntime,
    85  			Registry:          fakeRegistry,
    86  			StartupTimeout:    5 * time.Second,
    87  			Metrics:           launchMetrics,
    88  			PeerAddress:       "peer-address",
    89  			ConnectionHandler: fakeConnHandler,
    90  			CertGenerator:     fakeCertGenerator,
    91  		}
    92  	})
    93  
    94  	AfterEach(func() {
    95  		close(exitedCh)
    96  		close(extCCConnExited)
    97  	})
    98  
    99  	It("registers the chaincode as launching", func() {
   100  		err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   101  		Expect(err).NotTo(HaveOccurred())
   102  
   103  		Expect(fakeRegistry.LaunchingCallCount()).To(Equal(1))
   104  		cname := fakeRegistry.LaunchingArgsForCall(0)
   105  		Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   106  	})
   107  
   108  	Context("build does not return external chaincode info", func() {
   109  		BeforeEach(func() {
   110  			fakeRuntime.BuildReturns(nil, nil)
   111  		})
   112  
   113  		It("chaincode is launched", func() {
   114  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   115  			Expect(err).NotTo(HaveOccurred())
   116  
   117  			Expect(fakeRuntime.BuildCallCount()).To(Equal(1))
   118  			ccciArg := fakeRuntime.BuildArgsForCall(0)
   119  			Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   120  
   121  			Expect(fakeRuntime.StartCallCount()).To(Equal(1))
   122  		})
   123  	})
   124  
   125  	Context("build returns external chaincode info", func() {
   126  		BeforeEach(func() {
   127  			fakeRuntime.BuildReturns(&ccintf.ChaincodeServerInfo{Address: "ccaddress:12345"}, nil)
   128  		})
   129  
   130  		It("chaincode is not launched", func() {
   131  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   132  			Expect(err).NotTo(HaveOccurred())
   133  
   134  			Expect(fakeRuntime.BuildCallCount()).To(Equal(1))
   135  			ccciArg := fakeRuntime.BuildArgsForCall(0)
   136  			Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   137  
   138  			Expect(fakeRuntime.StartCallCount()).To(Equal(0))
   139  		})
   140  	})
   141  
   142  	It("starts the runtime for the chaincode", func() {
   143  		err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   144  		Expect(err).NotTo(HaveOccurred())
   145  
   146  		Expect(fakeRuntime.BuildCallCount()).To(Equal(1))
   147  		ccciArg := fakeRuntime.BuildArgsForCall(0)
   148  		Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   149  		Expect(fakeRuntime.StartCallCount()).To(Equal(1))
   150  		ccciArg, ccinfoArg := fakeRuntime.StartArgsForCall(0)
   151  		Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   152  
   153  		Expect(ccinfoArg).To(Equal(&ccintf.PeerConnection{Address: "peer-address", TLSConfig: &ccintf.TLSConfig{ClientCert: []byte("cert"), ClientKey: []byte("key"), RootCert: nil}}))
   154  	})
   155  
   156  	Context("tls is not enabled", func() {
   157  		BeforeEach(func() {
   158  			runtimeLauncher.CertGenerator = nil
   159  		})
   160  
   161  		It("starts the runtime for the chaincode with no TLS", func() {
   162  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   163  			Expect(err).NotTo(HaveOccurred())
   164  
   165  			Expect(fakeRuntime.BuildCallCount()).To(Equal(1))
   166  			ccciArg := fakeRuntime.BuildArgsForCall(0)
   167  			Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   168  			Expect(fakeRuntime.StartCallCount()).To(Equal(1))
   169  			ccciArg, ccinfoArg := fakeRuntime.StartArgsForCall(0)
   170  			Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   171  
   172  			Expect(ccinfoArg).To(Equal(&ccintf.PeerConnection{Address: "peer-address"}))
   173  		})
   174  	})
   175  
   176  	It("waits for the launch to complete", func() {
   177  		fakeRuntime.StartReturns(nil)
   178  
   179  		errCh := make(chan error, 1)
   180  		go func() { errCh <- runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler) }()
   181  
   182  		Consistently(errCh).ShouldNot(Receive())
   183  		launchState.Notify(nil)
   184  		Eventually(errCh).Should(Receive(BeNil()))
   185  	})
   186  
   187  	It("does not deregister the chaincode", func() {
   188  		err := runtimeLauncher.Launch("chaincode-name:chaincode-version, fakeStreamHandler", fakeStreamHandler)
   189  		Expect(err).NotTo(HaveOccurred())
   190  
   191  		Expect(fakeRegistry.DeregisterCallCount()).To(Equal(0))
   192  	})
   193  
   194  	It("records launch duration", func() {
   195  		err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   196  		Expect(err).NotTo(HaveOccurred())
   197  
   198  		Expect(fakeLaunchDuration.WithCallCount()).To(Equal(1))
   199  		labelValues := fakeLaunchDuration.WithArgsForCall(0)
   200  		Expect(labelValues).To(Equal([]string{
   201  			"chaincode", "chaincode-name:chaincode-version",
   202  			"success", "true",
   203  		}))
   204  		Expect(fakeLaunchDuration.ObserveArgsForCall(0)).NotTo(BeZero())
   205  		Expect(fakeLaunchDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0))
   206  	})
   207  
   208  	Context("when starting connection to external chaincode", func() {
   209  		BeforeEach(func() {
   210  			fakeRuntime.BuildReturns(&ccintf.ChaincodeServerInfo{Address: "peer-address"}, nil)
   211  		})
   212  		It("registers the chaincode as launching", func() {
   213  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   214  			Expect(err).NotTo(HaveOccurred())
   215  
   216  			Expect(fakeRegistry.LaunchingCallCount()).To(Equal(1))
   217  			cname := fakeRegistry.LaunchingArgsForCall(0)
   218  			Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   219  		})
   220  
   221  		It("starts the runtime for the chaincode", func() {
   222  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   223  			Expect(err).NotTo(HaveOccurred())
   224  
   225  			Expect(fakeRuntime.BuildCallCount()).To(Equal(1))
   226  			ccciArg := fakeRuntime.BuildArgsForCall(0)
   227  			Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   228  			Expect(fakeConnHandler.StreamCallCount()).To(Equal(1))
   229  			ccciArg, ccinfoArg, ccshandler := fakeConnHandler.StreamArgsForCall(0)
   230  			Expect(ccciArg).To(Equal("chaincode-name:chaincode-version"))
   231  
   232  			Expect(ccinfoArg).To(Equal(&ccintf.ChaincodeServerInfo{Address: "peer-address"}))
   233  			Expect(ccshandler).To(Equal(fakeStreamHandler))
   234  		})
   235  
   236  		It("does not deregister the chaincode", func() {
   237  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   238  			Expect(err).NotTo(HaveOccurred())
   239  
   240  			Expect(fakeRegistry.DeregisterCallCount()).To(Equal(0))
   241  		})
   242  
   243  		It("records launch duration", func() {
   244  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   245  			Expect(err).NotTo(HaveOccurred())
   246  
   247  			Expect(fakeLaunchDuration.WithCallCount()).To(Equal(1))
   248  			labelValues := fakeLaunchDuration.WithArgsForCall(0)
   249  			Expect(labelValues).To(Equal([]string{
   250  				"chaincode", "chaincode-name:chaincode-version",
   251  				"success", "true",
   252  			}))
   253  			Expect(fakeLaunchDuration.ObserveArgsForCall(0)).NotTo(BeZero())
   254  			Expect(fakeLaunchDuration.ObserveArgsForCall(0)).To(BeNumerically("<", 1.0))
   255  		})
   256  
   257  		Context("when starting the external connection fails", func() {
   258  			BeforeEach(func() {
   259  				fakeConnHandler.StreamReturns(errors.New("banana"))
   260  			})
   261  
   262  			It("returns a wrapped error", func() {
   263  				err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   264  				Expect(err).To(MatchError("connection to chaincode-name:chaincode-version failed: banana"))
   265  			})
   266  
   267  			It("notifies the LaunchState", func() {
   268  				runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   269  				Eventually(launchState.Done()).Should(BeClosed())
   270  				Expect(launchState.Err()).To(MatchError("connection to chaincode-name:chaincode-version failed: banana"))
   271  			})
   272  
   273  			It("records chaincode launch failures", func() {
   274  				runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   275  				Expect(fakeLaunchFailures.WithCallCount()).To(Equal(1))
   276  				labelValues := fakeLaunchFailures.WithArgsForCall(0)
   277  				Expect(labelValues).To(Equal([]string{
   278  					"chaincode", "chaincode-name:chaincode-version",
   279  				}))
   280  				Expect(fakeLaunchFailures.AddCallCount()).To(Equal(1))
   281  				Expect(fakeLaunchFailures.AddArgsForCall(0)).To(BeNumerically("~", 1.0))
   282  			})
   283  
   284  			It("deregisters the chaincode", func() {
   285  				runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   286  
   287  				Expect(fakeRegistry.DeregisterCallCount()).To(Equal(1))
   288  				cname := fakeRegistry.DeregisterArgsForCall(0)
   289  				Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   290  			})
   291  		})
   292  	})
   293  
   294  	Context("when starting the runtime fails", func() {
   295  		BeforeEach(func() {
   296  			fakeRuntime.StartReturns(errors.New("banana"))
   297  		})
   298  
   299  		It("returns a wrapped error", func() {
   300  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   301  			Expect(err).To(MatchError("error starting container: banana"))
   302  		})
   303  
   304  		It("notifies the LaunchState", func() {
   305  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   306  			Eventually(launchState.Done()).Should(BeClosed())
   307  			Expect(launchState.Err()).To(MatchError("error starting container: banana"))
   308  		})
   309  
   310  		It("records chaincode launch failures", func() {
   311  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   312  			Expect(fakeLaunchFailures.WithCallCount()).To(Equal(1))
   313  			labelValues := fakeLaunchFailures.WithArgsForCall(0)
   314  			Expect(labelValues).To(Equal([]string{
   315  				"chaincode", "chaincode-name:chaincode-version",
   316  			}))
   317  			Expect(fakeLaunchFailures.AddCallCount()).To(Equal(1))
   318  			Expect(fakeLaunchFailures.AddArgsForCall(0)).To(BeNumerically("~", 1.0))
   319  		})
   320  
   321  		It("deregisters the chaincode", func() {
   322  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   323  
   324  			Expect(fakeRegistry.DeregisterCallCount()).To(Equal(1))
   325  			cname := fakeRegistry.DeregisterArgsForCall(0)
   326  			Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   327  		})
   328  	})
   329  
   330  	Context("when the contaienr terminates before registration", func() {
   331  		BeforeEach(func() {
   332  			fakeRuntime.StartReturns(nil)
   333  			fakeRuntime.WaitReturns(-99, nil)
   334  		})
   335  
   336  		It("returns an error", func() {
   337  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   338  			Expect(err).To(MatchError("chaincode registration failed: container exited with -99"))
   339  		})
   340  
   341  		It("deregisters the chaincode", func() {
   342  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   343  
   344  			Expect(fakeRegistry.DeregisterCallCount()).To(Equal(1))
   345  			cname := fakeRegistry.DeregisterArgsForCall(0)
   346  			Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   347  		})
   348  	})
   349  
   350  	Context("when handler registration fails", func() {
   351  		BeforeEach(func() {
   352  			fakeRuntime.StartStub = func(string, *ccintf.PeerConnection) error {
   353  				launchState.Notify(errors.New("papaya"))
   354  				return nil
   355  			}
   356  		})
   357  
   358  		It("returns an error", func() {
   359  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   360  			Expect(err).To(MatchError("chaincode registration failed: papaya"))
   361  		})
   362  
   363  		It("deregisters the chaincode", func() {
   364  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   365  
   366  			Expect(fakeRegistry.DeregisterCallCount()).To(Equal(1))
   367  			cname := fakeRegistry.DeregisterArgsForCall(0)
   368  			Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   369  		})
   370  	})
   371  
   372  	Context("when the runtime startup times out", func() {
   373  		BeforeEach(func() {
   374  			fakeRuntime.StartReturns(nil)
   375  			runtimeLauncher.StartupTimeout = 250 * time.Millisecond
   376  		})
   377  
   378  		It("returns a meaningful error", func() {
   379  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   380  			Expect(err).To(MatchError("timeout expired while starting chaincode chaincode-name:chaincode-version for transaction"))
   381  		})
   382  
   383  		It("notifies the LaunchState", func() {
   384  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   385  			Eventually(launchState.Done()).Should(BeClosed())
   386  			Expect(launchState.Err()).To(MatchError("timeout expired while starting chaincode chaincode-name:chaincode-version for transaction"))
   387  		})
   388  
   389  		It("records chaincode launch timeouts", func() {
   390  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   391  			Expect(fakeLaunchTimeouts.WithCallCount()).To(Equal(1))
   392  			labelValues := fakeLaunchTimeouts.WithArgsForCall(0)
   393  			Expect(labelValues).To(Equal([]string{
   394  				"chaincode", "chaincode-name:chaincode-version",
   395  			}))
   396  			Expect(fakeLaunchTimeouts.AddCallCount()).To(Equal(1))
   397  			Expect(fakeLaunchTimeouts.AddArgsForCall(0)).To(BeNumerically("~", 1.0))
   398  		})
   399  
   400  		It("deregisters the chaincode", func() {
   401  			runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   402  
   403  			Expect(fakeRegistry.DeregisterCallCount()).To(Equal(1))
   404  			cname := fakeRegistry.DeregisterArgsForCall(0)
   405  			Expect(cname).To(Equal("chaincode-name:chaincode-version"))
   406  		})
   407  	})
   408  
   409  	Context("when the registry indicates the chaincode has already been started", func() {
   410  		BeforeEach(func() {
   411  			fakeRegistry.LaunchingReturns(launchState, true)
   412  		})
   413  
   414  		It("does not start the runtime for the chaincode", func() {
   415  			launchState.Notify(nil)
   416  
   417  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   418  			Expect(err).NotTo(HaveOccurred())
   419  
   420  			Expect(fakeRuntime.StartCallCount()).To(Equal(0))
   421  		})
   422  
   423  		It("waits for the launch to complete", func() {
   424  			fakeRuntime.StartReturns(nil)
   425  
   426  			errCh := make(chan error, 1)
   427  			go func() { errCh <- runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler) }()
   428  
   429  			Consistently(errCh).ShouldNot(Receive())
   430  			launchState.Notify(nil)
   431  			Eventually(errCh).Should(Receive(BeNil()))
   432  		})
   433  
   434  		Context("when the launch fails", func() {
   435  			BeforeEach(func() {
   436  				launchState.Notify(errors.New("gooey-guac"))
   437  			})
   438  
   439  			It("does not deregister the chaincode", func() {
   440  				err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   441  				Expect(err).To(MatchError("chaincode registration failed: gooey-guac"))
   442  				Expect(fakeRegistry.DeregisterCallCount()).To(Equal(0))
   443  			})
   444  		})
   445  	})
   446  
   447  	Context("when stopping the runtime fails while launching", func() {
   448  		BeforeEach(func() {
   449  			fakeRuntime.StartReturns(errors.New("whirled-peas"))
   450  			fakeRuntime.StopReturns(errors.New("applesauce"))
   451  		})
   452  
   453  		It("preserves the initial error", func() {
   454  			err := runtimeLauncher.Launch("chaincode-name:chaincode-version", fakeStreamHandler)
   455  			Expect(err).To(MatchError("error starting container: whirled-peas"))
   456  		})
   457  	})
   458  
   459  	It("stops the runtime for the chaincode", func() {
   460  		err := runtimeLauncher.Stop("chaincode-name:chaincode-version")
   461  		Expect(err).NotTo(HaveOccurred())
   462  
   463  		Expect(fakeRuntime.StopCallCount()).To(Equal(1))
   464  		ccidArg := fakeRuntime.StopArgsForCall(0)
   465  		Expect(ccidArg).To(Equal("chaincode-name:chaincode-version"))
   466  	})
   467  
   468  	Context("when stopping the runtime fails while stopping", func() {
   469  		BeforeEach(func() {
   470  			fakeRuntime.StopReturns(errors.New("liver-mush"))
   471  		})
   472  
   473  		It("preserves the initial error", func() {
   474  			err := runtimeLauncher.Stop("chaincode-name:chaincode-version")
   475  			Expect(err).To(MatchError("failed to stop chaincode chaincode-name:chaincode-version: liver-mush"))
   476  		})
   477  	})
   478  })