-
Notifications
You must be signed in to change notification settings - Fork 0
/
container.go
154 lines (120 loc) · 4.59 KB
/
container.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
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package container
import (
"io"
"sync"
"github.com/hyperledger/fabric/common/flogging"
"github.com/hyperledger/fabric/core/chaincode/persistence"
"github.com/hyperledger/fabric/core/container/ccintf"
"github.com/pkg/errors"
)
var vmLogger = flogging.MustGetLogger("container")
//go:generate counterfeiter -o mock/docker_builder.go --fake-name DockerBuilder . DockerBuilder
// DockerBuilder is what is exposed by the dockercontroller
type DockerBuilder interface {
Build(ccid string, metadata *persistence.ChaincodePackageMetadata, codePackageStream io.Reader) (Instance, error)
}
//go:generate counterfeiter -o mock/external_builder.go --fake-name ExternalBuilder . ExternalBuilder
// ExternalBuilder is what is exposed by the dockercontroller
type ExternalBuilder interface {
Build(ccid string, metadata []byte, codePackageStream io.Reader) (Instance, error)
}
//go:generate counterfeiter -o mock/instance.go --fake-name Instance . Instance
// Instance represents a built chaincode instance, because of the docker legacy, calling this a
// built 'container' would be very misleading, and going forward with the external launcher
// 'image' also seemed inappropriate. So, the vague 'Instance' is used here.
type Instance interface {
Start(peerConnection *ccintf.PeerConnection) error
ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error)
Stop() error
Wait() (int, error)
}
type UninitializedInstance struct{}
func (UninitializedInstance) Start(peerConnection *ccintf.PeerConnection) error {
return errors.Errorf("instance has not yet been built, cannot be started")
}
func (UninitializedInstance) ChaincodeServerInfo() (*ccintf.ChaincodeServerInfo, error) {
return nil, errors.Errorf("instance has not yet been built, cannot get chaincode server info")
}
func (UninitializedInstance) Stop() error {
return errors.Errorf("instance has not yet been built, cannot be stopped")
}
func (UninitializedInstance) Wait() (int, error) {
return 0, errors.Errorf("instance has not yet been built, cannot wait")
}
//go:generate counterfeiter -o mock/package_provider.go --fake-name PackageProvider . PackageProvider
// PackageProvider gets chaincode packages from the filesystem.
type PackageProvider interface {
GetChaincodePackage(packageID string) (md *persistence.ChaincodePackageMetadata, mdBytes []byte, codeStream io.ReadCloser, err error)
}
type Router struct {
ExternalBuilder ExternalBuilder
DockerBuilder DockerBuilder
containers map[string]Instance
PackageProvider PackageProvider
mutex sync.Mutex
}
func (r *Router) getInstance(ccid string) Instance {
r.mutex.Lock()
defer r.mutex.Unlock()
// Note, to resolve the locking problem which existed in the previous code, we never delete
// references from the map. In this way, it is safe to release the lock and operate
// on the returned reference
vm, ok := r.containers[ccid]
if !ok {
return UninitializedInstance{}
}
return vm
}
func (r *Router) Build(ccid string) error {
var instance Instance
if r.ExternalBuilder != nil {
// for now, the package ID we retrieve from the FS is always the ccid
// the chaincode uses for registration
_, mdBytes, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
if err != nil {
return errors.WithMessage(err, "failed to get chaincode package for external build")
}
defer codeStream.Close()
instance, err = r.ExternalBuilder.Build(ccid, mdBytes, codeStream)
if err != nil {
return errors.WithMessage(err, "external builder failed")
}
}
if instance == nil {
if r.DockerBuilder == nil {
return errors.New("no DockerBuilder, cannot build")
}
metadata, _, codeStream, err := r.PackageProvider.GetChaincodePackage(ccid)
if err != nil {
return errors.WithMessage(err, "failed to get chaincode package for docker build")
}
defer codeStream.Close()
instance, err = r.DockerBuilder.Build(ccid, metadata, codeStream)
if err != nil {
return errors.WithMessage(err, "docker build failed")
}
}
r.mutex.Lock()
defer r.mutex.Unlock()
if r.containers == nil {
r.containers = map[string]Instance{}
}
r.containers[ccid] = instance
return nil
}
func (r *Router) ChaincodeServerInfo(ccid string) (*ccintf.ChaincodeServerInfo, error) {
return r.getInstance(ccid).ChaincodeServerInfo()
}
func (r *Router) Start(ccid string, peerConnection *ccintf.PeerConnection) error {
return r.getInstance(ccid).Start(peerConnection)
}
func (r *Router) Stop(ccid string) error {
return r.getInstance(ccid).Stop()
}
func (r *Router) Wait(ccid string) (int, error) {
return r.getInstance(ccid).Wait()
}