diff --git a/cmd/release/main_test.go b/cmd/release/main_test.go index 156a364..7c776f4 100644 --- a/cmd/release/main_test.go +++ b/cmd/release/main_test.go @@ -48,8 +48,16 @@ var _ = Describe("Main", func() { indexPath := filepath.Join(tempDir, "statedb", "couchdb", "indexes", "indexOwner.json") Expect(indexPath).To(BeARegularFile()) + + assetPrivateDataCollectionIndexPath := filepath.Join(tempDir, "statedb", "couchdb", "collections", "assetCollection", "indexes", "indexOwner.json") + Expect(assetPrivateDataCollectionIndexPath).To(BeARegularFile(), "Private data index should be copied") + + fabCarPrivateDataCollectionIndexPath := filepath.Join(tempDir, "statedb", "couchdb", "collections", "fabCarCollection", "indexes", "indexOwner.json") + Expect(fabCarPrivateDataCollectionIndexPath).To(BeARegularFile(), "Private data index should be copied") + textPath := filepath.Join(tempDir, "statedb", "couchdb", "indexes", "test.txt") - Expect(textPath).NotTo(BeAnExistingFile()) + Expect(textPath).NotTo(BeAnExistingFile(), "Unexpected files should not be copied") + subdirPath := filepath.Join( tempDir, "statedb", @@ -58,6 +66,55 @@ var _ = Describe("Main", func() { "subdir", "indexOwner.json", ) - Expect(subdirPath).NotTo(BeAnExistingFile()) + Expect(subdirPath).NotTo(BeAnExistingFile(), "Files outside indexes directory should not be copied") + + privateDataCollectionSubdirPath := filepath.Join( + tempDir, + "statedb", + "couchdb", + "collections", + "fabCarCollection", + "subdir", + "indexes", + "indexOwner.json", + ) + Expect(privateDataCollectionSubdirPath).NotTo(BeAnExistingFile(), "Files outside indexes directory should not be copied") + + collectionsdCollectionPath := filepath.Join( + tempDir, + "statedb", + "couchdb", + "collectionsd", + "fabCarCollection", + "indexes", + "indexOwner.json", + ) + Expect(collectionsdCollectionPath).NotTo(BeAnExistingFile(), "Files outside indexes directory should not be copied") + + indexedCollectionSubdirPath := filepath.Join( + tempDir, + "statedb", + "couchdb", + "indexed", + "indexes", + "indexOwner.json", + ) + Expect(indexedCollectionSubdirPath).NotTo(BeAnExistingFile(), "Files outside indexes directory should not be copied") + + rootIndexOwnerJSONFile := filepath.Join( + tempDir, + "statedb", + "couchdb", + "indexOwner.json", + ) + Expect(rootIndexOwnerJSONFile).NotTo(BeAnExistingFile(), "Files outside indexes directory should not be copied") + + roottestTXTFile := filepath.Join( + tempDir, + "statedb", + "couchdb", + "test.txt", + ) + Expect(roottestTXTFile).NotTo(BeAnExistingFile(), "Files outside indexes directory should not be copied") }) }) diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json new file mode 100644 index 0000000..28ab876 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/assetCollection/indexes/indexOwner.json @@ -0,0 +1,11 @@ +{ + "index": { + "fields": [ + "objectType", + "owner" + ] + }, + "ddoc": "indexOwnerDoc", + "name": "indexOwner", + "type": "json" +} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/fabCarCollection/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/fabCarCollection/indexes/indexOwner.json new file mode 100644 index 0000000..c895391 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/fabCarCollection/indexes/indexOwner.json @@ -0,0 +1,11 @@ +{ + "index": { + "fields": [ + "model", + "owner" + ] + }, + "ddoc": "indexOwnerDoc", + "name": "indexOwner", + "type": "json" +} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/fabCarCollection/subdir/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/fabCarCollection/subdir/indexes/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collections/fabCarCollection/subdir/indexes/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/assetCollection/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/assetCollection/indexes/indexOwner.json new file mode 100644 index 0000000..28ab876 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/assetCollection/indexes/indexOwner.json @@ -0,0 +1,11 @@ +{ + "index": { + "fields": [ + "objectType", + "owner" + ] + }, + "ddoc": "indexOwnerDoc", + "name": "indexOwner", + "type": "json" +} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/fabCarCollection/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/fabCarCollection/indexes/indexOwner.json new file mode 100644 index 0000000..c895391 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/fabCarCollection/indexes/indexOwner.json @@ -0,0 +1,11 @@ +{ + "index": { + "fields": [ + "model", + "owner" + ] + }, + "ddoc": "indexOwnerDoc", + "name": "indexOwner", + "type": "json" +} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/fabCarCollection/subdir/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/fabCarCollection/subdir/indexes/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/collectionsd/fabCarCollection/subdir/indexes/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/subdir/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/subdir/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/subdir/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/test.txt b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/test.txt new file mode 100644 index 0000000..73709ba --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/indexed/test.txt @@ -0,0 +1 @@ +Testing diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/subdir/indexOwner.json b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/subdir/indexOwner.json new file mode 100644 index 0000000..305f090 --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/subdir/indexOwner.json @@ -0,0 +1 @@ +{"index":{"fields":["docType","owner"]},"ddoc":"indexOwnerDoc", "name":"indexOwner","type":"json"} diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/test.txt b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/test.txt new file mode 100644 index 0000000..73709ba --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/subdir/indexes/test.txt @@ -0,0 +1 @@ +Testing diff --git a/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/test.txt b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/test.txt new file mode 100644 index 0000000..73709ba --- /dev/null +++ b/cmd/release/testdata/buildwithindexes/META-INF/statedb/couchdb/test.txt @@ -0,0 +1 @@ +Testing diff --git a/internal/builder/release.go b/internal/builder/release.go index 7fea281..e4b6f60 100644 --- a/internal/builder/release.go +++ b/internal/builder/release.go @@ -19,7 +19,7 @@ func (r *Release) Run(ctx context.Context) error { logger.Debugln("Releasing chaincode...") // If CouchDB index definitions are required for the chaincode, release is - // responsible for placing the indexes into the statedb/couchdb/indexes + // responsible for placing the indexes into the statedb/couchdb/ // directory under RELEASE_OUTPUT_DIR. The indexes must have a .json // extension. err := util.CopyIndexFiles(logger, r.BuildOutputDirectory, r.ReleaseOutputDirectory) diff --git a/internal/util/copy.go b/internal/util/copy.go index 729df65..6eb1715 100644 --- a/internal/util/copy.go +++ b/internal/util/copy.go @@ -41,13 +41,9 @@ func CopyImageJSON(logger *log.CmdLogger, src, dest string) error { // CopyIndexFiles copies CouchDB index definitions from source to destination directories. func CopyIndexFiles(logger *log.CmdLogger, src, dest string) error { - indexDir := filepath.Join("statedb", "couchdb", "indexes") - indexSrcDir := filepath.Join(src, MetadataDir, indexDir) - indexDestDir := filepath.Join(dest, indexDir) - - logger.Debugf("Copying CouchDB index definitions from %s to %s", indexSrcDir, indexDestDir) + logger.Debugf("Copying couchdb index files from %s to %s", src, dest) - fileInfo, err := os.Lstat(indexSrcDir) + _, err := os.Lstat(src) if err != nil { if os.IsNotExist(err) { // indexes are optional @@ -57,17 +53,38 @@ func CopyIndexFiles(logger *log.CmdLogger, src, dest string) error { return err } - if !fileInfo.IsDir() { - return fmt.Errorf( - "CouchDB index definitions path %s is not a directory: %w", - indexSrcDir, - err, - ) - } + indexDir := filepath.Join("statedb", "couchdb") + indexSrcDir := filepath.Join(src, MetadataDir, indexDir) + indexDestDir := filepath.Join(dest, indexDir) opt := copy.Options{ - Skip: func(_ os.FileInfo, src, _ string) (bool, error) { - return !strings.HasSuffix(src, ".json"), nil + Skip: func(info os.FileInfo, src, _ string) (bool, error) { + logger.Debugf("Checking source copy path: %s", src) + if info.IsDir() { + skip, err := skipFolder(logger, indexSrcDir, src) + if err != nil { + return skip, fmt.Errorf( + "error checking if the folder is eligible to have a couchdb index: %s, %s: %w", + indexSrcDir, + src, + err, + ) + } + + return skip, nil + } + + skip, err := skipFile(logger, indexSrcDir, src) + if err != nil { + return skip, fmt.Errorf( + "error checking if the file is eligible to have a couchdb index: %s, %s: %w", + indexSrcDir, + src, + err, + ) + } + + return skip, nil }, } @@ -115,3 +132,78 @@ func CopyMetadataDir(logger *log.CmdLogger, src, dest string) error { return nil } + +// skipFile checks if the file will need to be skipped during indexes copy. +func skipFile(logger *log.CmdLogger, indexSrcDir, src string) (bool, error) { + path, err := filepath.Rel(indexSrcDir, src) + if err != nil { + logger.Debugf("error verifying relative path from: %s, src: %s", indexSrcDir, src) + + return true, fmt.Errorf( + "error verifying relative path from %s to %s: %w", + indexSrcDir, + src, + err, + ) + } + + if len(strings.Split(path, string(filepath.Separator))) == 1 { // JSON is in root couchdb folder + logger.Debugf("The JSON file in the root couchdb index folder, should skip: %s, src: %s", path, src) + + return true, nil + } + + if strings.HasSuffix(src, ".json") { + logger.Debugf("The JSON file is valid, should copy: %s, src: %s", path, src) + + return false, nil + } + + logger.Debugf("The JSON file is invalid, should skip: %s, src: %s", path, src) + + return true, nil +} + +// skipFolder checks if the folder will need to be skipped during indexes copy. +func skipFolder(logger *log.CmdLogger, indexSrcDir, src string) (bool, error) { + path, err := filepath.Rel(indexSrcDir, src) + if err != nil { + logger.Debugf("failed resolve relative path: %s, src: %s", indexSrcDir, src) + + return true, fmt.Errorf("failed resolve relative path %s to %s: %w", indexSrcDir, src, err) + } + + matchContainsPublicIndexFolder, _ := filepath.Match("indexes", path) + matchContainsPrivateDataCollectionFolder, _ := filepath.Match("collections", path) + matchPrivateDataCollectionFolder, _ := filepath.Match("collections/*", path) + matchPrivateDataCollectionIndexFolder, _ := filepath.Match("collections/*/indexes", path) + relativeFoldersLength := len(strings.Split(path, string(filepath.Separator))) + + logger.Debugf("Calculated relative path: %s. Total relative folders: %d", path, relativeFoldersLength) + logger.Debugf("Match pattern 'index': %t", matchContainsPublicIndexFolder) + logger.Debugf("Match pattern 'collections': %t", matchContainsPrivateDataCollectionFolder) + logger.Debugf("Match pattern 'collections/*': %t", matchPrivateDataCollectionFolder) + logger.Debugf("Match pattern 'collections/*/indexes': %t", matchPrivateDataCollectionIndexFolder) + + switch { + case relativeFoldersLength == 1 && (!matchContainsPublicIndexFolder && !matchContainsPrivateDataCollectionFolder): + logger.Debugf("Should skip folder") + + return true, nil + + case relativeFoldersLength == 2 && (!matchPrivateDataCollectionFolder): + logger.Debugf("Should skip folder") + + return true, nil + + case relativeFoldersLength == 3 && (!matchPrivateDataCollectionIndexFolder): + logger.Debugf("Should skip folder") + + return true, nil + + default: + logger.Debugf("Should not skip folder") + + return false, nil + } +}