Skip to content

Commit e3fb752

Browse files
committed
[FAB-7696] Add couch indexes to node cc install pkg
This change packages index metadata for node.js chaincode. It has the same behavior as the golang packaging recently introduced in FAB-7132. Allow indexes that support chaincode queries to be defined alongside chaincode. Any index defined in chaincode directory META-INF/statedb/couchdb/indexes will get added to the chaincode deployment spec code package, so that when the chaincode is both installed on a peer and instantiated on a channel, the indexes can be automatically deployed to couchdb. Change-Id: I1670e77508f496fd839a4df2480350184c870fab Signed-off-by: David Enyeart <enyeart@us.ibm.com>
1 parent 66d785b commit e3fb752

File tree

3 files changed

+140
-26
lines changed

3 files changed

+140
-26
lines changed

core/chaincode/platforms/node/platform.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ func (nodePlatform *Platform) GetDeploymentPayload(spec *pb.ChaincodeSpec) ([]by
143143
return nil, errors.New("ChaincodeSpec's path cannot be empty")
144144
}
145145

146-
if strings.LastIndex(folder, "/") == len(folder)-1 {
146+
// trim trailing slash if it exists
147+
if folder[len(folder)-1] == '/' {
147148
folder = folder[:len(folder)-1]
148149
}
149150

core/container/util/writer.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,18 @@ var javaExcludeFileTypes = map[string]bool{
2828
".class": true,
2929
}
3030

31+
// WriteFolderToTarPackage writes source files to a tarball.
32+
// This utility is used for node js chaincode packaging, but not golang chaincode.
33+
// Golang chaincode has more sophisticated file packaging, as implemented in golang/platform.go.
3134
func WriteFolderToTarPackage(tw *tar.Writer, srcPath string, excludeDir string, includeFileTypeMap map[string]bool, excludeFileTypeMap map[string]bool) error {
3235
fileCount := 0
3336
rootDirectory := srcPath
37+
38+
// trim trailing slash if it was passed
39+
if rootDirectory[len(rootDirectory)-1] == '/' {
40+
rootDirectory = rootDirectory[:len(rootDirectory)-1]
41+
}
42+
3443
vmLogger.Infof("rootDirectory = %s", rootDirectory)
3544

3645
//append "/" if necessary
@@ -75,8 +84,14 @@ func WriteFolderToTarPackage(tw *tar.Writer, srcPath string, excludeDir string,
7584
}
7685
}
7786

78-
newPath := fmt.Sprintf("src%s", path[rootDirLen:])
79-
//newPath := path[len(rootDirectory):]
87+
var newPath string
88+
// if file is metadata, keep the /META-INF directory, e.g: META-INF/statedb/couchdb/indexes/indexOwner.json
89+
// otherwise file is source code, put it in /src dir, e.g: src/marbles_chaincode.js
90+
if strings.HasPrefix(path, filepath.Join(rootDirectory, "META-INF")) {
91+
newPath = path[rootDirLen+1:]
92+
} else {
93+
newPath = fmt.Sprintf("src%s", path[rootDirLen:])
94+
}
8095

8196
err = WriteFileToPackage(path, newPath, tw)
8297
if err != nil {
@@ -120,6 +135,7 @@ func WriteJavaProjectToPackage(tw *tar.Writer, srcPath string) error {
120135

121136
//WriteFileToPackage writes a file to the tarball
122137
func WriteFileToPackage(localpath string, packagepath string, tw *tar.Writer) error {
138+
vmLogger.Debug("Writing file to tarball:", packagepath)
123139
fd, err := os.Open(localpath)
124140
if err != nil {
125141
return fmt.Errorf("%s: %s", localpath, err)

core/container/util/writer_test.go

Lines changed: 120 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"bytes"
1212
"compress/gzip"
1313
"fmt"
14+
"io"
1415
"io/ioutil"
1516
"os"
1617
"path/filepath"
@@ -42,6 +43,7 @@ func Test_WriteFileToPackage(t *testing.T) {
4243
// Read the file from the archive and check the name and file content
4344
r := bytes.NewReader(buf.Bytes())
4445
gr, err1 := gzip.NewReader(r)
46+
defer gr.Close()
4547
assert.NoError(t, err1, "Error creating a gzip reader")
4648
tr := tar.NewReader(gr)
4749
header, err2 := tr.Next()
@@ -110,6 +112,7 @@ func Test_WriteStreamToPackage(t *testing.T) {
110112
// Read the file from the archive and check the name and file content
111113
br := bytes.NewReader(buf.Bytes())
112114
gr, err1 := gzip.NewReader(br)
115+
defer gr.Close()
113116
assert.NoError(t, err1, "Error creating a gzip reader")
114117
tr := tar.NewReader(gr)
115118
header, err2 := tr.Next()
@@ -124,14 +127,12 @@ func Test_WriteStreamToPackage(t *testing.T) {
124127
"file content from the archive is not same as original file content")
125128
}
126129

127-
func Test_WriteFolderToTarPackage(t *testing.T) {
128-
buf := bytes.NewBuffer(nil)
129-
gw := gzip.NewWriter(buf)
130-
tw := tar.NewWriter(gw)
130+
// Success case 1: with include and exclude file types and without exclude dir
131+
func Test_WriteFolderToTarPackage1(t *testing.T) {
131132

132-
// Success case 1: with include and exclude file types and without exclude dir
133133
gopath := os.Getenv("GOPATH")
134134
gopath = filepath.SplitList(gopath)[0]
135+
135136
srcPath := filepath.Join(gopath, "src",
136137
"github.com/hyperledger/fabric/examples/chaincode/java/SimpleSample")
137138
filePath := "src/src/main/java/example/SimpleSample.java"
@@ -142,41 +143,137 @@ func Test_WriteFolderToTarPackage(t *testing.T) {
142143
".xml": true,
143144
}
144145

145-
err := WriteFolderToTarPackage(tw, srcPath, "",
146-
includeFileTypes, excludeFileTypes)
147-
assert.NoError(t, err, "Error writing folder to package")
148-
149-
tw.Close()
150-
gw.Close()
146+
tarBytes := createTestTar(t, srcPath, "", includeFileTypes, excludeFileTypes)
151147

152148
// Read the file from the archive and check the name
153-
br := bytes.NewReader(buf.Bytes())
149+
br := bytes.NewReader(tarBytes)
154150
gr, err1 := gzip.NewReader(br)
151+
defer gr.Close()
155152
assert.NoError(t, err1, "Error creating a gzip reader")
156153
tr := tar.NewReader(gr)
157154
header, err2 := tr.Next()
158155
assert.NoError(t, err2, "Error getting the file from the tar")
159156
assert.Equal(t, filePath, header.Name,
160157
"Name of the file read from the archive is not same as the file added to the archive")
158+
}
161159

162-
// Success case 2: with exclude dir and no include file types
163-
srcPath = filepath.Join(gopath, "src",
160+
// Success case 2: with exclude dir and no include file types
161+
func Test_WriteFolderToTarPackage2(t *testing.T) {
162+
163+
gopath := os.Getenv("GOPATH")
164+
gopath = filepath.SplitList(gopath)[0]
165+
166+
srcPath := filepath.Join(gopath, "src",
164167
"github.com/hyperledger/fabric/examples/chaincode/java")
165-
tarw := tar.NewWriter(bytes.NewBuffer(nil))
166-
defer tarw.Close()
167-
err = WriteFolderToTarPackage(tarw, srcPath, "SimpleSample",
168-
nil, excludeFileTypes)
168+
excludeFileTypes := map[string]bool{
169+
".xml": true,
170+
}
171+
172+
createTestTar(t, srcPath, "SimpleSample", nil, excludeFileTypes)
173+
}
174+
175+
// Success case 3: with chaincode metadata in META-INF directory
176+
func Test_WriteFolderToTarPackage3(t *testing.T) {
177+
178+
gopath := os.Getenv("GOPATH")
179+
gopath = filepath.SplitList(gopath)[0]
180+
181+
// Note - go chaincode does not use WriteFolderToTarPackage(),
182+
// but we can still use the go example for unit test,
183+
// since there are no node chaincode examples in fabric repos
184+
srcPath := filepath.Join(gopath, "src",
185+
"github.com/hyperledger/fabric/examples/chaincode/go/marbles02")
186+
filePath := "META-INF/statedb/couchdb/indexes/indexOwner.json"
187+
188+
tarBytes := createTestTar(t, srcPath, "", nil, nil)
189+
190+
// Read the files from the archive and check for the metadata index file
191+
br := bytes.NewReader(tarBytes)
192+
gr, err := gzip.NewReader(br)
193+
defer gr.Close()
194+
assert.NoError(t, err, "Error creating a gzip reader")
195+
tr := tar.NewReader(gr)
196+
var foundIndexArtifact bool
197+
for {
198+
header, err := tr.Next()
199+
if err == io.EOF { // No more entries
200+
break
201+
}
202+
assert.NoError(t, err, "Error getting Next() file in tar")
203+
t.Logf("Found file in tar: %s", header.Name)
204+
if header.Name == filePath {
205+
foundIndexArtifact = true
206+
break
207+
}
208+
}
209+
assert.True(t, foundIndexArtifact, "should have found statedb index artifact in marbles02 META-INF directory")
210+
}
211+
212+
// Success case 4: with chaincode metadata in META-INF directory, pass trailing slash in srcPath
213+
func Test_WriteFolderToTarPackage4(t *testing.T) {
214+
215+
gopath := os.Getenv("GOPATH")
216+
gopath = filepath.SplitList(gopath)[0]
217+
218+
// Note - go chaincode does not use WriteFolderToTarPackage(),
219+
// but we can still use the go example for unit test,
220+
// since there are no node chaincode examples in fabric repos
221+
srcPath := filepath.Join(gopath, "src",
222+
"github.com/hyperledger/fabric/examples/chaincode/go/marbles02")
223+
srcPath = srcPath + "/"
224+
filePath := "META-INF/statedb/couchdb/indexes/indexOwner.json"
225+
226+
tarBytes := createTestTar(t, srcPath, "", nil, nil)
227+
228+
// Read the files from the archive and check for the metadata index file
229+
br := bytes.NewReader(tarBytes)
230+
gr, err := gzip.NewReader(br)
231+
defer gr.Close()
232+
assert.NoError(t, err, "Error creating a gzip reader")
233+
tr := tar.NewReader(gr)
234+
var foundIndexArtifact bool
235+
for {
236+
header, err := tr.Next()
237+
if err == io.EOF { // No more entries
238+
break
239+
}
240+
assert.NoError(t, err, "Error getting Next() file in tar")
241+
t.Logf("Found file in tar: %s", header.Name)
242+
if header.Name == filePath {
243+
foundIndexArtifact = true
244+
break
245+
}
246+
}
247+
assert.True(t, foundIndexArtifact, "should have found statedb index artifact in marbles02 META-INF directory")
248+
}
249+
250+
func createTestTar(t *testing.T, srcPath string, excludeDir string, includeFileTypeMap map[string]bool, excludeFileTypeMap map[string]bool) []byte {
251+
buf := bytes.NewBuffer(nil)
252+
gw := gzip.NewWriter(buf)
253+
tw := tar.NewWriter(gw)
254+
255+
err := WriteFolderToTarPackage(tw, srcPath, "", includeFileTypeMap, excludeFileTypeMap)
169256
assert.NoError(t, err, "Error writing folder to package")
170257

171-
// Failure case 1: no files in directory
172-
srcPath = filepath.Join(gopath, "src",
258+
tw.Close()
259+
gw.Close()
260+
return buf.Bytes()
261+
}
262+
263+
// Failure case 1: no files in directory
264+
func Test_WriteFolderToTarPackageFailure1(t *testing.T) {
265+
gopath := os.Getenv("GOPATH")
266+
gopath = filepath.SplitList(gopath)[0]
267+
268+
srcPath := filepath.Join(gopath, "src",
173269
"github.com/hyperledger/fabric/core/container/util",
174270
fmt.Sprintf("%d", os.Getpid()))
175271
os.Mkdir(srcPath, os.ModePerm)
176272
defer os.Remove(srcPath)
177-
tarw = tar.NewWriter(bytes.NewBuffer(nil))
178-
defer tarw.Close()
179-
err = WriteFolderToTarPackage(tw, srcPath, "", nil, nil)
273+
274+
tw := tar.NewWriter(bytes.NewBuffer(nil))
275+
defer tw.Close()
276+
err := WriteFolderToTarPackage(tw, srcPath, "", nil, nil)
180277
assert.Contains(t, err.Error(), "no source files found")
181278
}
182279

0 commit comments

Comments
 (0)