diff --git a/Makefile b/Makefile index 5571d16fc8b..faef64d8026 100644 --- a/Makefile +++ b/Makefile @@ -281,7 +281,7 @@ dist: dist-clean dist/$(MARCH) .PHONY: dist-all dist-all: dist-clean $(RELEASE_PLATFORMS:%=dist/%) -dist/%: release/% ccaasbuilder +dist/%: release/% externalbuilder ccaasbuilder mkdir -p release/$(@F)/config cp -r sampleconfig/*.yaml release/$(@F)/config cd release/$(@F) && tar -czvf hyperledger-fabric-$(@F).$(PROJECT_VERSION).tar.gz * @@ -351,6 +351,11 @@ ccaasbuilder-clean/%: $(eval platform = $(patsubst ccaasbuilder/%,%,$@) ) cd ccaas_builder && rm -rf $(strip $(platform)) +.PHONY: externalbuilder-clean +externalbuilder-clean/%: + $(eval platform = $(patsubst externalbuilder/%,%,$@) ) + cd external_builder && rm -rf $(strip $(platform)) + .PHONY: ccaasbuilder ccaasbuilder/%: ccaasbuilder-clean $(eval platform = $(patsubst ccaasbuilder/%,%,$@) ) @@ -361,8 +366,20 @@ ccaasbuilder/%: ccaasbuilder-clean cd ccaas_builder && go test -v ./cmd/build && GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildvcs=false -o ../release/$(strip $(platform))/builders/ccaas/bin/ ./cmd/build/ cd ccaas_builder && go test -v ./cmd/release && GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildvcs=false -o ../release/$(strip $(platform))/builders/ccaas/bin/ ./cmd/release/ +.PHONY: externalbuilder +externalbuilder/%: externalbuilder-clean + $(eval platform = $(patsubst externalbuilder/%,%,$@) ) + $(eval GOOS = $(word 1,$(subst -, ,$(platform)))) + $(eval GOARCH = $(word 2,$(subst -, ,$(platform)))) + @mkdir -p release/$(strip $(platform))/builders/external/bin + cd external_builder && go test -v ./cmd/detect && GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildvcs=false -o ../release/$(strip $(platform))/builders/external/bin/ ./cmd/detect/ + cd external_builder && go test -v ./cmd/build && GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildvcs=false -o ../release/$(strip $(platform))/builders/external/bin/ ./cmd/build/ + cd external_builder && go test -v ./cmd/release && GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildvcs=false -o ../release/$(strip $(platform))/builders/external/bin/ ./cmd/release/ + ccaasbuilder: ccaasbuilder/$(MARCH) +externalbuilder: externalbuilder/$(MARCH) + .PHONY: scan scan: scan-govulncheck diff --git a/external_builder/.gitignore b/external_builder/.gitignore new file mode 100644 index 00000000000..c6ee5451750 --- /dev/null +++ b/external_builder/.gitignore @@ -0,0 +1,2 @@ +#SPDX-License-Identifier: Apache-2.0 +bin/* diff --git a/external_builder/cmd/build/main.go b/external_builder/cmd/build/main.go new file mode 100644 index 00000000000..f7c97acb150 --- /dev/null +++ b/external_builder/cmd/build/main.go @@ -0,0 +1,193 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + "strings" + "text/template" + + "github.com/otiai10/copy" + "github.com/pkg/errors" +) + +var logger = log.New(os.Stderr, "", 0) + +type chaincodeMetadata struct { + Type string `json:"type"` +} + +// Connection structure is used to represent the +// connection.json file that is supplied in the +// chaincode package. +type connection struct { + Address string `json:"address"` + DialTimeout string `json:"dial_timeout"` + TLS bool `json:"tls_required"` + ClientAuth bool `json:"client_auth_required"` + RootCert string `json:"root_cert"` + ClientKey string `json:"client_key"` + ClientCert string `json:"client_cert"` +} + +type Config struct { + PeerName string +} + +func main() { + logger.Println("::Build") + + if err := run(); err != nil { + logger.Printf("::Error: %v\n", err) + os.Exit(1) + } + + logger.Printf("::Build phase completed") + +} + +func run() error { + if len(os.Args) < 4 { + return fmt.Errorf("incorrect number of arguments") + } + + sourceDir, metadataDir, outputDir := os.Args[1], os.Args[2], os.Args[3] + + connectionSrcFile := filepath.Join(sourceDir, "/connection.json") + metadataFile := filepath.Clean(filepath.Join(metadataDir, "metadata.json")) + connectionDestFile := filepath.Join(outputDir, "/connection.json") + metainfoSrcDir := filepath.Join(sourceDir, "META-INF") + metainfoDestDir := filepath.Join(outputDir, "META-INF") + + // Process and check the metadata file, then copy to the output location + if _, err := os.Stat(metadataFile); err != nil { + return errors.WithMessagef(err, "%s not found ", metadataFile) + } + + metadataFileContents, cause := os.ReadFile(metadataFile) + if cause != nil { + return errors.WithMessagef(cause, "%s file not readable", metadataFile) + } + + var metadata chaincodeMetadata + if err := json.Unmarshal(metadataFileContents, &metadata); err != nil { + return errors.WithMessage(err, "Unable to parse JSON") + } + + if strings.ToLower(metadata.Type) != "external" { + return fmt.Errorf("chaincode type should be external, it is %s", metadata.Type) + } + + if err := copy.Copy(metadataDir, outputDir); err != nil { + return fmt.Errorf("failed to copy build metadata folder: %s", err) + } + + if _, err := os.Stat(metainfoSrcDir); !os.IsNotExist(err) { + if err := copy.Copy(metainfoSrcDir, metainfoDestDir); err != nil { + return fmt.Errorf("failed to copy build META-INF folder: %s", err) + } + } + + // Process and update the connections file + fileInfo, err := os.Stat(connectionSrcFile) + if err != nil { + return errors.WithMessagef(err, "%s not found ", connectionSrcFile) + } + + connectionFileContents, err := os.ReadFile(connectionSrcFile) + if err != nil { + return err + } + + // read the connection.json file into structure to process + var connectionData connection + if err := json.Unmarshal(connectionFileContents, &connectionData); err != nil { + return err + } + + // Treat each of the string fields in the connection.json as Go template + // strings. They can be fixed strings, but if they are templates + // then the JSON string that is defined in CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG + // is used as the 'context' to parse the string + + updatedConnection := connection{} + var cfg map[string]interface{} + + cfgString := os.Getenv("CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG") + if cfgString != "" { + if err := json.Unmarshal([]byte(cfgString), &cfg); err != nil { + return fmt.Errorf("Failed to unmarshal %s", err) + } + } + + updatedConnection.Address, err = execTempl(cfg, connectionData.Address) + if err != nil { + return fmt.Errorf("Failed to parse the Address field template: %s", err) + } + + updatedConnection.DialTimeout, err = execTempl(cfg, connectionData.DialTimeout) + if err != nil { + return fmt.Errorf("Failed to parse the DialTimeout field template: %s", err) + } + + // if connection is TLS Enabled, updated with the correct information + // no other information is needed for the no-TLS case, so the default can be assumed + // to be good + if connectionData.TLS { + updatedConnection.TLS = true + updatedConnection.ClientAuth = connectionData.ClientAuth + + updatedConnection.RootCert, err = execTempl(cfg, connectionData.RootCert) + if err != nil { + return fmt.Errorf("Failed to parse the RootCert field template: %s", err) + } + updatedConnection.ClientKey, err = execTempl(cfg, connectionData.ClientKey) + if err != nil { + return fmt.Errorf("Failed to parse the ClientKey field template: %s", err) + } + updatedConnection.ClientCert, err = execTempl(cfg, connectionData.ClientCert) + if err != nil { + return fmt.Errorf("Failed to parse the ClientCert field template: %s", err) + } + } + + updatedConnectionBytes, err := json.Marshal(updatedConnection) + if err != nil { + return fmt.Errorf("failed to marshal updated connection.json file: %s", err) + } + + err = os.WriteFile(connectionDestFile, updatedConnectionBytes, fileInfo.Mode()) + if err != nil { + return err + } + + return nil + +} + +// execTempl is a helper function to process a template against a string, and return a string +func execTempl(cfg map[string]interface{}, inputStr string) (string, error) { + + t, err := template.New("").Option("missingkey=error").Parse(inputStr) + if err != nil { + fmt.Printf("Failed to parse the template: %s", err) + return "", err + } + + buf := &bytes.Buffer{} + err = t.Execute(buf, cfg) + if err != nil { + return "", err + } + + return buf.String(), nil +} diff --git a/external_builder/cmd/build/main_test.go b/external_builder/cmd/build/main_test.go new file mode 100644 index 00000000000..4b0fae56641 --- /dev/null +++ b/external_builder/cmd/build/main_test.go @@ -0,0 +1,321 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "os" + "os/exec" + "path" + "testing" + "time" + + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" +) + +func TestArguements(t *testing.T) { + + testCases := map[string]struct { + exitCode int + args []string + }{ + "toofew1": { + exitCode: 1, + args: []string{"metadatadir"}, + }, + "toofew2": { + exitCode: 1, + args: []string{"wibble", "wibble"}, + }, + "twomany": { + exitCode: 1, + args: []string{"wibble", "wibble", "wibble", "wibble"}, + }, + } + + // Build ledger binary + gt := NewWithT(t) + buildCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/build") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + cmd := exec.Command(buildCmd, testCase.args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(testCase.exitCode)) + }) + } +} + +func TestGoodPath(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/build") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + // create a basic structure of a chaincode + os.MkdirAll(path.Join(testPath, "in-builder-dir", "META-INF"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "in-metadata-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + connectionJson := path.Join(testPath, "in-builder-dir", "connection.json") + file, err := os.Create(connectionJson) + gt.Expect(err).NotTo(HaveOccurred()) + file.WriteString(`{ + "address": "audit-trail-ccaas:9999", + "dial_timeout": "10s", + "tls_required": false + }`) + + metadataJson := path.Join(testPath, "in-metadata-dir", "metadata.json") + fileMetadata, err := os.Create(metadataJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileMetadata.WriteString(`{ + "type":"ccaas" + }`) + + indexJson := path.Join(testPath, "in-builder-dir", "META-INF", "indexes.json") + fileIndex, err := os.Create(indexJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileIndex.WriteString(`{ + "couchindexes": "gohere" + }`) + + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "in-metadata-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(0)) + + // check that the files have been copied + chkfile := path.Join(testPath, "out-release-dir", "connection.json") + _, err = os.Stat(chkfile) + gt.Expect(err).NotTo(HaveOccurred()) + + chkfile = path.Join(testPath, "out-release-dir", "META-INF", "indexes.json") + _, err = os.Stat(chkfile) + gt.Expect(err).NotTo(HaveOccurred()) +} + +func TestTemplating(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/build") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + // create a basic structure of the chaincode to use + os.MkdirAll(path.Join(testPath, "in-builder-dir", "META-INF"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "in-metadata-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + connectionJson := path.Join(testPath, "in-builder-dir", "connection.json") + file, err := os.Create(connectionJson) + gt.Expect(err).NotTo(HaveOccurred()) + file.WriteString(`{ + "address": "{{.address}}:9999", + "dial_timeout": "10s", + "tls_required": true, + "root_cert":"{{.root_cert}}", + "client_key":"{{.client_key}}", + "client_cert":"{{.client_cert}}", + "client_auth_required":true + }`) + + metadataJson := path.Join(testPath, "in-metadata-dir", "metadata.json") + fileMetadata, err := os.Create(metadataJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileMetadata.WriteString(`{ + "type":"ccaas" + }`) + + // call 'build' with the environment set for the templating to be tested + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "in-metadata-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + env := os.Environ() + env = append(env, `CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"address":"URLAddress","root_cert":"a root cert","client_key":"a client key","client_cert":"a client cert"}`) + cmd.Env = env + + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(0)) + + // check that the files have been copied + chkfile := path.Join(testPath, "out-release-dir", "connection.json") + _, err = os.Stat(chkfile) + gt.Expect(err).NotTo(HaveOccurred()) + + // check that the file has the exepected contents + connectionFileContents, err := os.ReadFile(chkfile) + gt.Expect(err).NotTo(HaveOccurred()) + + expectedJson := `{ + "address": "URLAddress:9999", + "dial_timeout": "10s", + "tls_required": true, + "root_cert":"a root cert", + "client_key":"a client key", + "client_cert":"a client cert", + "client_auth_required":true + }` + + gt.Expect(string(connectionFileContents)).To(MatchJSON(expectedJson)) +} + +func TestTemplatingFailure(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/build") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + // create a basic structure of the chaincode to use + os.MkdirAll(path.Join(testPath, "in-builder-dir", "META-INF"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "in-metadata-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + connectionJson := path.Join(testPath, "in-builder-dir", "connection.json") + file, err := os.Create(connectionJson) + gt.Expect(err).NotTo(HaveOccurred()) + file.WriteString(`{ + "address": "{{.address}}:9999", + "dial_timeout": "10s", + "tls_required": true, + "root_cert":"{{.root_cert}}", + "client_key":"{{.client_key}}", + "client_cert":"{{.client_cert}}", + "client_auth_required":true + }`) + + metadataJson := path.Join(testPath, "in-metadata-dir", "metadata.json") + fileMetadata, err := os.Create(metadataJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileMetadata.WriteString(`{ + "type":"ccaas" + }`) + + // call 'build' with the environment set for the templating to be tested + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "in-metadata-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + env := os.Environ() + env = append(env, `CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG={"address":"URLAddress","root_cert":"a root cert"}`) + cmd.Env = env + + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(1)) + gt.Expect(session.Err).To(gbytes.Say("Failed to parse the ClientKey field template")) +} + +func TestMissingConnection(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/build") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + // create a basic structure of a chaincode + os.MkdirAll(path.Join(testPath, "in-builder-dir", "META-INF"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "in-metadata-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + metadataJson := path.Join(testPath, "in-metadata-dir", "metadata.json") + fileMetadata, err := os.Create(metadataJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileMetadata.WriteString(`{ + "type":"ccaas" + }`) + + indexJson := path.Join(testPath, "in-builder-dir", "META-INF", "indexes.json") + fileIndex, err := os.Create(indexJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileIndex.WriteString(`{ + "couchindexes": "gohere" + }`) + + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "in-metadata-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(1)) + gt.Expect(session.Err).To(gbytes.Say("connection.json not found")) +} + +func TestMissingMetadata(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/build") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + // create a basic structure of a chaincode + os.MkdirAll(path.Join(testPath, "in-builder-dir", "META-INF"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "in-metadata-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + connectionJson := path.Join(testPath, "in-builder-dir", "connection.json") + file, err := os.Create(connectionJson) + gt.Expect(err).NotTo(HaveOccurred()) + file.WriteString(`{ + "address": "audit-trail-ccaas:9999", + "dial_timeout": "10s", + "tls_required": false + }`) + + indexJson := path.Join(testPath, "in-builder-dir", "META-INF", "indexes.json") + fileIndex, err := os.Create(indexJson) + gt.Expect(err).NotTo(HaveOccurred()) + fileIndex.WriteString(`{ + "couchindexes": "gohere" + }`) + + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "in-metadata-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(1)) + gt.Expect(session.Err).To(gbytes.Say("metadata.json not found")) +} diff --git a/external_builder/cmd/detect/main.go b/external_builder/cmd/detect/main.go new file mode 100644 index 00000000000..01bd15f3807 --- /dev/null +++ b/external_builder/cmd/detect/main.go @@ -0,0 +1,68 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "encoding/json" + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" +) + +var logger = log.New(os.Stderr, "", 0) + +func main() { + logger.Println("::Detect") + + if err := run(); err != nil { + logger.Printf("::Error: %v\n", err) + os.Exit(1) + } + + logger.Printf("::Type detected as external") +} + +type chaincodeMetadata struct { + Type string `json:"type"` +} + +func run() error { + if len(os.Args) < 3 { + return fmt.Errorf("too few arguments") + } + + chaincodeMetaData := os.Args[2] + + // Check metadata file's existence + metadataFile := filepath.Join(chaincodeMetaData, "metadata.json") + if _, err := os.Stat(metadataFile); err != nil { + return errors.WithMessagef(err, "%s not found ", metadataFile) + } + + // Read the metadata file + mdbytes, err := os.ReadFile(metadataFile) + if err != nil { + return errors.WithMessagef(err, "%s not readable", metadataFile) + } + + var metadata chaincodeMetadata + err = json.Unmarshal(mdbytes, &metadata) + if err != nil { + return errors.WithMessage(err, "Unable to parse the metadata.json file") + } + + if strings.ToLower(metadata.Type) != "external" { + return fmt.Errorf("chaincode type not supported: %s", metadata.Type) + } + + // returning nil indicates to the peer a successful detection + return nil +} diff --git a/external_builder/cmd/detect/main_test.go b/external_builder/cmd/detect/main_test.go new file mode 100644 index 00000000000..bd6a18f9b7a --- /dev/null +++ b/external_builder/cmd/detect/main_test.go @@ -0,0 +1,73 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" + + "os/exec" + "testing" + "time" +) + +func TestArugments(t *testing.T) { + + testCases := map[string]struct { + exitCode int + args []string + }{ + "toofew": { + exitCode: 1, + args: []string{"metadatadir"}, + }, + "twomany": { + exitCode: 1, + args: []string{"wibble", "wibble", "wibble"}, + }, + "validtype": { + exitCode: 0, + args: []string{"na", "testdata/validtype"}, + }, + "wrongtype": { + exitCode: 1, + args: []string{"na", "testdata/wrongtype"}, + }, + } + + // Build ledger binary + gt := NewWithT(t) + detectCmd, err := gexec.Build("github.com/hyperledger/fabric/external_builder/cmd/detect") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + cmd := exec.Command(detectCmd, testCase.args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(testCase.exitCode)) + }) + } +} + +func TestMissingFile(t *testing.T) { + gt := NewWithT(t) + + testPath := t.TempDir() + + detectCmd, err := gexec.Build("github.com/hyperledger/fabric/external_builder/cmd/detect") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + cmd := exec.Command(detectCmd, []string{"na", testPath}...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(1)) + gt.Expect(session.Err).To(gbytes.Say("metadata.json not found")) +} diff --git a/external_builder/cmd/detect/testdata/validtype/metadata.json b/external_builder/cmd/detect/testdata/validtype/metadata.json new file mode 100644 index 00000000000..8b617429efe --- /dev/null +++ b/external_builder/cmd/detect/testdata/validtype/metadata.json @@ -0,0 +1,4 @@ +{ + "type": "external", + "label": "audit-trail" +} diff --git a/external_builder/cmd/detect/testdata/wrongtype/metadata.json b/external_builder/cmd/detect/testdata/wrongtype/metadata.json new file mode 100644 index 00000000000..bbe3f36e7bd --- /dev/null +++ b/external_builder/cmd/detect/testdata/wrongtype/metadata.json @@ -0,0 +1,4 @@ +{ + "type": "fred", + "label": "audit-trail" +} \ No newline at end of file diff --git a/external_builder/cmd/release/main.go b/external_builder/cmd/release/main.go new file mode 100644 index 00000000000..84f0cd42411 --- /dev/null +++ b/external_builder/cmd/release/main.go @@ -0,0 +1,107 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "fmt" + "io" + "log" + "os" + "path/filepath" + + "github.com/otiai10/copy" +) + +var logger = log.New(os.Stderr, "", 0) + +type chaincodeMetadata struct { + Type string `json:"type"` +} + +// Connection structure is used to represent the +// connection.json file that is supplied in the +// chaincode package. +type connection struct { + Address string `json:"address"` + DialTimeout string `json:"dial_timeout"` + TLS bool `json:"tls_required"` + ClientAuth bool `json:"client_auth_required"` + RootCert string `json:"root_cert"` + ClientKey string `json:"client_key"` + ClientCert string `json:"client_cert"` +} + +func main() { + logger.Println("::Release") + + if err := run(); err != nil { + logger.Printf("::Error: %v\n", err) + os.Exit(1) + } + logger.Printf("::Release phase completed") + +} + +func run() error { + if len(os.Args) < 3 { + return fmt.Errorf("incorrect number of arguments") + } + + builderOutputDir, releaseDir := os.Args[1], os.Args[2] + connectionSrcFile := filepath.Join(builderOutputDir, "/connection.json") + + connectionDir := filepath.Join(releaseDir, "chaincode/server/") + connectionDestFile := filepath.Join(releaseDir, "chaincode/server/connection.json") + + metadataDir := filepath.Join(builderOutputDir, "META-INF/statedb") + metadataDestDir := filepath.Join(releaseDir, "statedb") + if _, err := os.Stat(metadataDir); !os.IsNotExist(err) { + if err := copy.Copy(metadataDir, metadataDestDir); err != nil { + return fmt.Errorf("failed to copy metadataDir directory folder: %s", err) + } + } + + // Process and update the connections file + _, err := os.Stat(connectionSrcFile) + if err != nil { + return fmt.Errorf("connection.json not found in source folder: %s", err) + } + + err = os.MkdirAll(connectionDir, 0750) + if err != nil { + return fmt.Errorf("failed to create target folder for connection.json: %s", err) + } + + if err = Copy(connectionSrcFile, connectionDestFile); err != nil { + return err + } + + return nil + +} + +// Copy the src file to dst. Any existing file will be overwritten and will not +// copy file attributes. +func Copy(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + return out.Close() +} diff --git a/external_builder/cmd/release/main_test.go b/external_builder/cmd/release/main_test.go new file mode 100644 index 00000000000..0bddb532475 --- /dev/null +++ b/external_builder/cmd/release/main_test.go @@ -0,0 +1,112 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package main + +import ( + "os" + "os/exec" + "path" + "testing" + "time" + + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" +) + +func TestArugments(t *testing.T) { + + testCases := map[string]struct { + exitCode int + args []string + }{ + "toofew": { + exitCode: 1, + args: []string{"metadatadir"}, + }, + "twomany": { + exitCode: 1, + args: []string{"wibble", "wibble", "wibble"}, + }, + } + + // Build ledger binary + gt := NewWithT(t) + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/release") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + for testName, testCase := range testCases { + t.Run(testName, func(t *testing.T) { + cmd := exec.Command(releaseCmd, testCase.args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(testCase.exitCode)) + }) + } +} + +func TestGoodPath(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/release") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + os.MkdirAll(path.Join(testPath, "in-builder-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + connectionJson := path.Join(testPath, "in-builder-dir", "connection.json") + file, err := os.Create(connectionJson) + gt.Expect(err).NotTo(HaveOccurred()) + file.WriteString(`{ + "address": "audit-trail-ccaas:9999", + "dial_timeout": "10s", + "tls_required": false + }`) + + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(0)) + + // check that the files have been copied + destConnectionJson := path.Join(testPath, "out-release-dir", "chaincode", "server", "connection.json") + _, err = os.Stat(destConnectionJson) + gt.Expect(err).NotTo(HaveOccurred()) + +} + +func TestMissingConnection(t *testing.T) { + gt := NewWithT(t) + + releaseCmd, err := gexec.Build("github.com/hyperledger/fabric/ccaas_builder/cmd/release") + gt.Expect(err).NotTo(HaveOccurred()) + defer gexec.CleanupBuildArtifacts() + + testPath := t.TempDir() + + os.MkdirAll(path.Join(testPath, "in-builder-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + os.MkdirAll(path.Join(testPath, "out-release-dir"), 0755) + gt.Expect(err).NotTo(HaveOccurred()) + + args := []string{path.Join(testPath, "in-builder-dir"), path.Join(testPath, "out-release-dir")} + cmd := exec.Command(releaseCmd, args...) + session, err := gexec.Start(cmd, nil, nil) + gt.Expect(err).NotTo(HaveOccurred()) + gt.Eventually(session, 5*time.Second).Should(gexec.Exit(1)) + + gt.Expect(session.Err).To(gbytes.Say("connection.json not found")) +} diff --git a/external_builder/go.mod b/external_builder/go.mod new file mode 100644 index 00000000000..bdef61bf69a --- /dev/null +++ b/external_builder/go.mod @@ -0,0 +1,9 @@ +module github.com/hyperledger/fabric/ccaas_builder + +go 1.16 + +require ( + github.com/onsi/gomega v1.17.0 + github.com/otiai10/copy v1.6.0 + github.com/pkg/errors v0.9.1 +) diff --git a/external_builder/go.sum b/external_builder/go.sum new file mode 100644 index 00000000000..d2476bfd800 --- /dev/null +++ b/external_builder/go.sum @@ -0,0 +1,100 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/otiai10/copy v1.6.0 h1:IinKAryFFuPONZ7cm6T6E2QX/vcJwSnlaA5lfoaXIiQ= +github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= +github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= +github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= +github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= +github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E= +github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/images/peer/Dockerfile b/images/peer/Dockerfile index a6a753869d4..5b6a66d9ad0 100644 --- a/images/peer/Dockerfile +++ b/images/peer/Dockerfile @@ -42,6 +42,7 @@ ADD . . RUN make peer GO_TAGS=${GO_TAGS} FABRIC_VER=${FABRIC_VER} RUN make ccaasbuilder +RUN make externalbuilder ############################################################################### @@ -68,6 +69,7 @@ COPY --from=builder sampleconfig/msp ${FABRIC_CFG_PATH}/msp COPY --from=builder sampleconfig/core.yaml ${FABRIC_CFG_PATH}/core.yaml COPY --from=builder release/${TARGETOS}-${TARGETARCH}/builders/ccaas/bin /opt/hyperledger/ccaas_builder/bin +COPY --from=builder release/${TARGETOS}-${TARGETARCH}/builders/external/bin /opt/hyperledger/external_builder/bin VOLUME /etc/hyperledger/fabric VOLUME /var/hyperledger