-
Notifications
You must be signed in to change notification settings - Fork 8.8k
/
runtime_launcher.go
158 lines (135 loc) · 4.51 KB
/
runtime_launcher.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package chaincode
import (
"strconv"
"time"
"github.com/hyperledger/fabric/core/chaincode/accesscontrol"
"github.com/hyperledger/fabric/core/chaincode/extcc"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/pkg/errors"
)
// LaunchRegistry tracks launching chaincode instances.
type LaunchRegistry interface {
Launching(ccid string) (launchState *LaunchState, started bool)
Deregister(ccid string) error
}
// ConnectionHandler handles the `Chaincode` client connection
type ConnectionHandler interface {
Stream(ccid string, ccinfo *ccintf.ChaincodeServerInfo, sHandler extcc.StreamHandler) error
}
// RuntimeLauncher is responsible for launching chaincode runtimes.
type RuntimeLauncher struct {
Runtime Runtime
Registry LaunchRegistry
StartupTimeout time.Duration
Metrics *LaunchMetrics
PeerAddress string
CACert []byte
CertGenerator CertGenerator
ConnectionHandler ConnectionHandler
}
// CertGenerator generates client certificates for chaincode.
type CertGenerator interface {
// Generate returns a certificate and private key and associates
// the hash of the certificates with the given chaincode name
Generate(ccName string) (*accesscontrol.CertAndPrivKeyPair, error)
}
func (r *RuntimeLauncher) ChaincodeClientInfo(ccid string) (*ccintf.PeerConnection, error) {
var tlsConfig *ccintf.TLSConfig
if r.CertGenerator != nil {
certKeyPair, err := r.CertGenerator.Generate(string(ccid))
if err != nil {
return nil, errors.WithMessagef(err, "failed to generate TLS certificates for %s", ccid)
}
tlsConfig = &ccintf.TLSConfig{
ClientCert: certKeyPair.Cert,
ClientKey: certKeyPair.Key,
RootCert: r.CACert,
}
}
return &ccintf.PeerConnection{
Address: r.PeerAddress,
TLSConfig: tlsConfig,
}, nil
}
func (r *RuntimeLauncher) Launch(ccid string, streamHandler extcc.StreamHandler) error {
var startFailCh chan error
var timeoutCh <-chan time.Time
startTime := time.Now()
launchState, alreadyStarted := r.Registry.Launching(ccid)
if !alreadyStarted {
startFailCh = make(chan error, 1)
timeoutCh = time.NewTimer(r.StartupTimeout).C
go func() {
// go through the build process to obtain connecion information
ccservinfo, err := r.Runtime.Build(ccid)
if err != nil {
startFailCh <- errors.WithMessage(err, "error building chaincode")
return
}
// chaincode server model indicated... proceed to connect to CC
if ccservinfo != nil {
if err = r.ConnectionHandler.Stream(ccid, ccservinfo, streamHandler); err != nil {
startFailCh <- errors.WithMessagef(err, "connection to %s failed", ccid)
return
}
launchState.Notify(errors.Errorf("connection to %s terminated", ccid))
return
}
// default peer-as-server model... compute connection information for CC callback
// and proceed to launch chaincode
ccinfo, err := r.ChaincodeClientInfo(ccid)
if err != nil {
startFailCh <- errors.WithMessage(err, "could not get connection info")
return
}
if ccinfo == nil {
startFailCh <- errors.New("could not get connection info")
return
}
if err = r.Runtime.Start(ccid, ccinfo); err != nil {
startFailCh <- errors.WithMessage(err, "error starting container")
return
}
exitCode, err := r.Runtime.Wait(ccid)
if err != nil {
launchState.Notify(errors.Wrap(err, "failed to wait on container exit"))
}
launchState.Notify(errors.Errorf("container exited with %d", exitCode))
}()
}
var err error
select {
case <-launchState.Done():
err = errors.WithMessage(launchState.Err(), "chaincode registration failed")
case err = <-startFailCh:
launchState.Notify(err)
r.Metrics.LaunchFailures.With("chaincode", ccid).Add(1)
case <-timeoutCh:
err = errors.Errorf("timeout expired while starting chaincode %s for transaction", ccid)
launchState.Notify(err)
r.Metrics.LaunchTimeouts.With("chaincode", ccid).Add(1)
}
success := true
if err != nil && !alreadyStarted {
success = false
chaincodeLogger.Debugf("stopping due to error while launching: %+v", err)
defer r.Registry.Deregister(ccid)
}
r.Metrics.LaunchDuration.With(
"chaincode", ccid,
"success", strconv.FormatBool(success),
).Observe(time.Since(startTime).Seconds())
chaincodeLogger.Debug("launch complete")
return err
}
func (r *RuntimeLauncher) Stop(ccid string) error {
err := r.Runtime.Stop(ccid)
if err != nil {
return errors.WithMessagef(err, "failed to stop chaincode %s", ccid)
}
return nil
}