@@ -25,32 +25,33 @@ import (
25
25
)
26
26
27
27
var (
28
+ // DefaultEnvWhitelist enumerates the list of environment variables that are
29
+ // implicitly propagated to external builder and launcher commands.
28
30
DefaultEnvWhitelist = []string {"LD_LIBRARY_PATH" , "LIBPATH" , "PATH" , "TMPDIR" }
29
- logger = flogging .MustGetLogger ("chaincode.externalbuilder" )
30
- )
31
31
32
- const MetadataFile = "metadata.json"
32
+ logger = flogging .MustGetLogger ("chaincode.externalbuilder" )
33
+ )
33
34
35
+ // BuildInfo contains metadata is that is saved to the local file system with the
36
+ // assets generated by an external builder. This is used to associate build output
37
+ // with the builder that generated it.
34
38
type BuildInfo struct {
39
+ // BuilderName is the user provided name of the external builder.
35
40
BuilderName string `json:"builder_name"`
36
41
}
37
42
43
+ // A Detector is responsible for orchestrating the external builder detection and
44
+ // build process.
38
45
type Detector struct {
46
+ // DurablePath is the file system location where chaincode assets are persisted.
39
47
DurablePath string
40
- Builders []* Builder
48
+ // Builders are the builders that detect and build processing will use.
49
+ Builders []* Builder
41
50
}
42
51
43
- func (d * Detector ) Detect (buildContext * BuildContext ) * Builder {
44
- for _ , builder := range d .Builders {
45
- if builder .Detect (buildContext ) {
46
- return builder
47
- }
48
- }
49
- return nil
50
- }
51
-
52
- // CachedBuild returns a build instance that was already built, or nil, or
53
- // when an unexpected error is encountered, an error.
52
+ // CachedBuild returns a build instance that was already built or nil when no
53
+ // instance has been found. An error is returned only when an unexpected
54
+ // condition is encountered.
54
55
func (d * Detector ) CachedBuild (ccid string ) (* Instance , error ) {
55
56
durablePath := filepath .Join (d .DurablePath , SanitizeCCIDPath (ccid ))
56
57
_ , err := os .Stat (durablePath )
@@ -67,9 +68,8 @@ func (d *Detector) CachedBuild(ccid string) (*Instance, error) {
67
68
return nil , errors .WithMessagef (err , "could not read '%s' for build info" , buildInfoPath )
68
69
}
69
70
70
- buildInfo := & BuildInfo {}
71
- err = json .Unmarshal (buildInfoData , buildInfo )
72
- if err != nil {
71
+ var buildInfo BuildInfo
72
+ if err := json .Unmarshal (buildInfoData , & buildInfo ); err != nil {
73
73
return nil , errors .WithMessagef (err , "malformed build info at '%s'" , buildInfoPath )
74
74
}
75
75
@@ -87,11 +87,16 @@ func (d *Detector) CachedBuild(ccid string) (*Instance, error) {
87
87
return nil , errors .Errorf ("chaincode '%s' was already built with builder '%s', but that builder is no longer available" , ccid , buildInfo .BuilderName )
88
88
}
89
89
90
+ // Build executes the external builder detect and build process.
91
+ //
92
+ // Before running the detect and build process, the detector first checks the
93
+ // durable path for the results of a previous build for the provided package.
94
+ // If found, the detect and build process is skipped and the existing instance
95
+ // is returned.
90
96
func (d * Detector ) Build (ccid string , md * persistence.ChaincodePackageMetadata , codeStream io.Reader ) (* Instance , error ) {
97
+ // A small optimization: prevent exploding the build package out into the
98
+ // file system unless there are external builders defined.
91
99
if len (d .Builders ) == 0 {
92
- // A small optimization, especially while the launcher feature is under development
93
- // let's not explode the build package out into the filesystem unless there are
94
- // external builders to run against it.
95
100
return nil , nil
96
101
}
97
102
@@ -110,7 +115,7 @@ func (d *Detector) Build(ccid string, md *persistence.ChaincodePackageMetadata,
110
115
}
111
116
defer buildContext .Cleanup ()
112
117
113
- builder := d .Detect (buildContext )
118
+ builder := d .detect (buildContext )
114
119
if builder == nil {
115
120
logger .Debugf ("no external builder detected for %s" , ccid )
116
121
return nil , nil
@@ -128,7 +133,7 @@ func (d *Detector) Build(ccid string, md *persistence.ChaincodePackageMetadata,
128
133
129
134
err = os .Mkdir (durablePath , 0700 )
130
135
if err != nil {
131
- return nil , errors .WithMessagef (err , "could not create dir '%s' to persist build ouput " , durablePath )
136
+ return nil , errors .WithMessagef (err , "could not create dir '%s' to persist build output " , durablePath )
132
137
}
133
138
134
139
buildInfo , err := json .Marshal (& BuildInfo {
@@ -166,6 +171,17 @@ func (d *Detector) Build(ccid string, md *persistence.ChaincodePackageMetadata,
166
171
}, nil
167
172
}
168
173
174
+ func (d * Detector ) detect (buildContext * BuildContext ) * Builder {
175
+ for _ , builder := range d .Builders {
176
+ if builder .Detect (buildContext ) {
177
+ return builder
178
+ }
179
+ }
180
+ return nil
181
+ }
182
+
183
+ // BuildContext holds references to the various assets locations necessary to
184
+ // execute the detect, build, release, and run programs for external builders
169
185
type BuildContext struct {
170
186
CCID string
171
187
Metadata * persistence.ChaincodePackageMetadata
@@ -176,6 +192,11 @@ type BuildContext struct {
176
192
BldDir string
177
193
}
178
194
195
+ // NewBuildContext creates the directories required to runt he external
196
+ // build process and extracts the chaincode package assets.
197
+ //
198
+ // Users of the BuildContext must call Cleanup when the build process is
199
+ // complete to remove the transient file system assets.
179
200
func NewBuildContext (ccid string , md * persistence.ChaincodePackageMetadata , codePackage io.Reader ) (bc * BuildContext , err error ) {
180
201
scratchDir , err := ioutil .TempDir ("" , "fabric-" + SanitizeCCIDPath (ccid ))
181
202
if err != nil {
@@ -229,12 +250,15 @@ func NewBuildContext(ccid string, md *persistence.ChaincodePackageMetadata, code
229
250
}, nil
230
251
}
231
252
253
+ // Cleanup removes the build context artifacts.
232
254
func (bc * BuildContext ) Cleanup () {
233
255
os .RemoveAll (bc .ScratchDir )
234
256
}
235
257
236
258
var pkgIDreg = regexp .MustCompile ("[<>:\" /\\ \\ |\\ ?\\ *&]" )
237
259
260
+ // SanitizeCCIDPath is used to ensure that special characters are removed from
261
+ // file names.
238
262
func SanitizeCCIDPath (ccid string ) string {
239
263
return pkgIDreg .ReplaceAllString (ccid , "-" )
240
264
}
@@ -256,16 +280,18 @@ func writeMetadataFile(ccid string, md *persistence.ChaincodePackageMetadata, ds
256
280
return errors .Wrap (err , "failed to marshal build metadata into JSON" )
257
281
}
258
282
259
- return ioutil .WriteFile (filepath .Join (dst , MetadataFile ), mdBytes , 0700 )
283
+ return ioutil .WriteFile (filepath .Join (dst , "metadata.json" ), mdBytes , 0700 )
260
284
}
261
285
286
+ // A Builder is used to interact with an external chaincode builder and launcher.
262
287
type Builder struct {
263
288
EnvWhitelist []string
264
289
Location string
265
290
Logger * flogging.FabricLogger
266
291
Name string
267
292
}
268
293
294
+ // CreateBuilders will construct builders from the peer configuration.
269
295
func CreateBuilders (builderConfs []peer.ExternalBuilder ) []* Builder {
270
296
var builders []* Builder
271
297
for _ , builderConf := range builderConfs {
@@ -279,11 +305,12 @@ func CreateBuilders(builderConfs []peer.ExternalBuilder) []*Builder {
279
305
return builders
280
306
}
281
307
308
+ // Detect runs the `detect` script.
282
309
func (b * Builder ) Detect (buildContext * BuildContext ) bool {
283
310
detect := filepath .Join (b .Location , "bin" , "detect" )
284
311
cmd := b .NewCommand (detect , buildContext .SourceDir , buildContext .MetadataDir )
285
312
286
- err := RunCommand ( b . Logger , cmd )
313
+ err := b . runCommand ( cmd )
287
314
if err != nil {
288
315
logger .Debugf ("builder '%s' detect failed: %s" , b .Name , err )
289
316
return false
@@ -292,18 +319,20 @@ func (b *Builder) Detect(buildContext *BuildContext) bool {
292
319
return true
293
320
}
294
321
322
+ // Build runs the `build` script.
295
323
func (b * Builder ) Build (buildContext * BuildContext ) error {
296
324
build := filepath .Join (b .Location , "bin" , "build" )
297
325
cmd := b .NewCommand (build , buildContext .SourceDir , buildContext .MetadataDir , buildContext .BldDir )
298
326
299
- err := RunCommand ( b . Logger , cmd )
327
+ err := b . runCommand ( cmd )
300
328
if err != nil {
301
329
return errors .Wrapf (err , "external builder '%s' failed" , b .Name )
302
330
}
303
331
304
332
return nil
305
333
}
306
334
335
+ // Release runs the `release` script.
307
336
func (b * Builder ) Release (buildContext * BuildContext ) error {
308
337
release := filepath .Join (b .Location , "bin" , "release" )
309
338
@@ -312,52 +341,58 @@ func (b *Builder) Release(buildContext *BuildContext) error {
312
341
b .Logger .Debugf ("Skipping release step for '%s' as no release binary found" , buildContext .CCID )
313
342
return nil
314
343
}
315
-
316
344
if err != nil {
317
345
return errors .WithMessagef (err , "could not stat release binary '%s'" , release )
318
346
}
319
347
320
348
cmd := b .NewCommand (release , buildContext .BldDir , buildContext .ReleaseDir )
321
-
322
- if err := RunCommand ( b . Logger , cmd ); err != nil {
349
+ err = b . runCommand ( cmd )
350
+ if err != nil {
323
351
return errors .Wrapf (err , "builder '%s' release failed" , b .Name )
324
352
}
325
353
326
354
return nil
327
355
}
328
356
329
- // RunConfig is serialized to disk when launching
330
- type RunConfig struct {
357
+ // runConfig is serialized to disk when launching.
358
+ type runConfig struct {
331
359
CCID string `json:"chaincode_id"`
332
360
PeerAddress string `json:"peer_address"`
333
- ClientCert string `json:"client_cert"` // PEM encoded client certifcate
361
+ ClientCert string `json:"client_cert"` // PEM encoded client certificate
334
362
ClientKey string `json:"client_key"` // PEM encoded client key
335
363
RootCert string `json:"root_cert"` // PEM encoded peer chaincode certificate
336
364
}
337
365
338
- func ( b * Builder ) Run ( ccid , bldDir string , peerConnection * ccintf.PeerConnection ) ( * Session , error ) {
339
- lc := & RunConfig {
340
- PeerAddress : peerConnection .Address ,
341
- CCID : ccid ,
366
+ func newRunConfig ( ccid string , peerConnection * ccintf.PeerConnection ) runConfig {
367
+ var tlsConfig ccintf. TLSConfig
368
+ if peerConnection .TLSConfig != nil {
369
+ tlsConfig = * peerConnection . TLSConfig
342
370
}
343
371
344
- if peerConnection .TLSConfig != nil {
345
- lc .ClientCert = string (peerConnection .TLSConfig .ClientCert )
346
- lc .ClientKey = string (peerConnection .TLSConfig .ClientKey )
347
- lc .RootCert = string (peerConnection .TLSConfig .RootCert )
372
+ return runConfig {
373
+ PeerAddress : peerConnection .Address ,
374
+ CCID : ccid ,
375
+ ClientCert : string (tlsConfig .ClientCert ),
376
+ ClientKey : string (tlsConfig .ClientKey ),
377
+ RootCert : string (tlsConfig .RootCert ),
348
378
}
379
+ }
349
380
381
+ // Run starts the `run` script and returns a Session that can be used to
382
+ // signal it and wait for termination.
383
+ func (b * Builder ) Run (ccid , bldDir string , peerConnection * ccintf.PeerConnection ) (* Session , error ) {
350
384
launchDir , err := ioutil .TempDir ("" , "fabric-run" )
351
385
if err != nil {
352
386
return nil , errors .WithMessage (err , "could not create temp run dir" )
353
387
}
354
388
355
- marshaledLC , err := json .Marshal (lc )
389
+ rc := newRunConfig (ccid , peerConnection )
390
+ marshaledRC , err := json .Marshal (rc )
356
391
if err != nil {
357
392
return nil , errors .WithMessage (err , "could not marshal run config" )
358
393
}
359
394
360
- if err := ioutil .WriteFile (filepath .Join (launchDir , "chaincode.json" ), marshaledLC , 0600 ); err != nil {
395
+ if err := ioutil .WriteFile (filepath .Join (launchDir , "chaincode.json" ), marshaledRC , 0600 ); err != nil {
361
396
return nil , errors .WithMessage (err , "could not write root cert" )
362
397
}
363
398
@@ -377,6 +412,15 @@ func (b *Builder) Run(ccid, bldDir string, peerConnection *ccintf.PeerConnection
377
412
return sess , nil
378
413
}
379
414
415
+ // runCommand runs a command and waits for it to complete.
416
+ func (b * Builder ) runCommand (cmd * exec.Cmd ) error {
417
+ sess , err := Start (b .Logger , cmd )
418
+ if err != nil {
419
+ return err
420
+ }
421
+ return sess .Wait ()
422
+ }
423
+
380
424
// NewCommand creates an exec.Cmd that is configured to prune the calling
381
425
// environment down to the environment variables specified in the external
382
426
// builder's EnvironmentWhitelist and the DefaultEnvWhitelist.
@@ -408,11 +452,3 @@ func contains(envWhiteList []string, key string) bool {
408
452
}
409
453
return false
410
454
}
411
-
412
- func RunCommand (logger * flogging.FabricLogger , cmd * exec.Cmd ) error {
413
- sess , err := Start (logger , cmd )
414
- if err != nil {
415
- return err
416
- }
417
- return sess .Wait ()
418
- }
0 commit comments