Skip to content

Commit

Permalink
Adding a dedicated external builder
Browse files Browse the repository at this point in the history
Signed-off-by: Matthew B White <whitemat@uk.ibm.com>
(cherry picked from commit 4b39cb2)
  • Loading branch information
mbwhite authored and denyeart committed Dec 9, 2021
1 parent 654a02b commit e0bb139
Show file tree
Hide file tree
Showing 17 changed files with 1,047 additions and 11 deletions.
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ integration-test: integration-test-prereqs
./scripts/run-integration-tests.sh

.PHONY: integration-test-prereqs
integration-test-prereqs: gotool.ginkgo baseos-docker ccenv-docker docker-thirdparty
integration-test-prereqs: gotool.ginkgo baseos-docker ccenv-docker docker-thirdparty ccaasbuilder

.PHONY: unit-test
unit-test: unit-test-clean docker-thirdparty-couchdb
Expand Down Expand Up @@ -280,7 +280,7 @@ dist: dist-clean dist/$(MARCH)

.PHONY: dist-all
dist-all: dist-clean $(RELEASE_PLATFORMS:%=dist/%)
dist/%: release/%
dist/%: release/% 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 @@ -344,3 +344,13 @@ spaces:
.PHONY: docs
docs:
@docker run --rm -v $$(pwd):/docs n42org/tox:3.4.0 sh -c 'cd /docs && tox -e docs'

.PHONY: ccaasbuilder-clean
ccaasbuilder-clean:
rm -rf $(MARCH:%=release/%)/bin/ccaas_builder

.PHONY: ccaasbuilder
ccaasbuilder: ccaasbuilder-clean
cd ccaas_builder && go test -v ./cmd/detect && go build -o ../$(MARCH:%=release/%)/bin/ccaas_builder/bin/ ./cmd/detect/
cd ccaas_builder && go test -v ./cmd/build && go build -o ../$(MARCH:%=release/%)/bin/ccaas_builder/bin/ ./cmd/build/
cd ccaas_builder && go test -v ./cmd/release && go build -o ../$(MARCH:%=release/%)/bin/ccaas_builder/bin/ ./cmd/release/
2 changes: 2 additions & 0 deletions ccaas_builder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#SPDX-License-Identifier: Apache-2.0
bin/*
194 changes: 194 additions & 0 deletions ccaas_builder/cmd/build/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"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 := ioutil.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) != "ccaas" {
return fmt.Errorf("chaincode type should be ccaas, 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 := ioutil.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 = ioutil.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
}

0 comments on commit e0bb139

Please sign in to comment.