Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding external chaincode binaries to the base peer image WIP #4825

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down Expand Up @@ -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/%,%,$@) )
Expand All @@ -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

Expand Down
2 changes: 2 additions & 0 deletions external_builder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#SPDX-License-Identifier: Apache-2.0
bin/*
193 changes: 193 additions & 0 deletions external_builder/cmd/build/main.go
Original file line number Diff line number Diff line change
@@ -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
}