195 lines
5.3 KiB
Go
195 lines
5.3 KiB
Go
/*
|
|
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
|
|
}
|