diff --git a/README.md b/README.md index 12d9501126..7cbceadd79 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,17 @@ cd $GOPATH/src/github.com/hyperledger/fabric-sdk-go/test/integration/ go test ``` +#### Using default config + +Default SDK Go configurations are found in the code under /pkg/config/config.yaml + +To override the default in non Dev environment, set the default path in the following environment variable: + +**FABRIC_SDK_CONFIG_PATH**=/path/to/default/config yaml(without specifying the yaml file name) + +This path value must be a directory. It must contain a default 'config.yaml' file. +Note that this default config is used only if environment configuration yaml file is missing to ensure all environment variables are created regardless of their values. + #### Testing with Local Build of Fabric (Advanced) Alternatively you can build and run Fabric on your own box using the following commands: diff --git a/pkg/config/config.go b/pkg/config/config.go index 5eefb55983..bea12ed9fb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,6 +18,8 @@ import ( "strings" "time" + "path/filepath" + "github.com/hyperledger/fabric-sdk-go/api/apiconfig" bccspFactory "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/factory" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/bccsp/pkcs11" @@ -56,6 +58,10 @@ func InitConfigWithCmdRoot(configFile string, cmdRootPrefix string) (*Config, er myViper.AutomaticEnv() replacer := strings.NewReplacer(".", "_") myViper.SetEnvKeyReplacer(replacer) + err := loadDefaultConfig() + if err != nil { + return nil, err + } if configFile != "" { // create new viper myViper.SetConfigFile(configFile) @@ -85,6 +91,23 @@ func InitConfigWithCmdRoot(configFile string, cmdRootPrefix string) (*Config, er return &Config{tlsCertPool: x509.NewCertPool()}, nil } +// load Default confid +func loadDefaultConfig() error { + // get Environment Default Config Path + defaultPath := os.Getenv("FABRIC_SDK_CONFIG_PATH") + if defaultPath != "" { // if set, use it to load default config + myViper.AddConfigPath(strings.Replace(defaultPath, "$GOPATH", os.Getenv("GOPATH"), -1)) + } else { // else fallback to default DEV path + devPath := filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "hyperledger", "fabric-sdk-go", "pkg", "config") + myViper.AddConfigPath(devPath) + } + err := myViper.ReadInConfig() // Find and read the config file + if err != nil { // Handle errors reading the config file + return fmt.Errorf("fatal error config file: %s", err) + } + return nil +} + // Client returns the Client config func (c *Config) Client() (*apiconfig.ClientConfig, error) { config, err := c.NetworkConfig() diff --git a/pkg/config/config.yaml b/pkg/config/config.yaml new file mode 100755 index 0000000000..8ca50da586 --- /dev/null +++ b/pkg/config/config.yaml @@ -0,0 +1,239 @@ +# +# Copyright SecureKey Technologies Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# +# Default GO SDK Configuration +# +# The network connection profile provides client applications the information about the target +# blockchain network that are necessary for the applications to interact with it. These are all +# knowledge that must be acquired from out-of-band sources. This file provides such a source. +# +name: "default-network" + +# +# Any properties with an "x-" prefix will be treated as application-specific, exactly like how naming +# in HTTP headers or swagger properties work. The SDK will simply ignore these fields and leave +# them for the applications to process. This is a mechanism for different components of an application +# to exchange information that are not part of the standard schema described below. In particular, +# the "x-type" property with the "hlfv1" value example below is used by Hyperledger Composer to +# determine the type of Fabric networks (v0.6 vs. v1.0) it needs to work with. +# +x-type: "hlfv1" +x-loggingLevel: info + +# +# Describe what the target network is/does. +# +description: "The network description" + +# +# Schema version of the content. Used by the SDK to apply the corresponding parsing rules. +# +version: 1.0.0 + +# +# The client section used by GO SDK. +# +client: + # Which organization does this application instance belong to? The value must be the name of an org + # defined under "organizations" + organization: match_value_with_one_of_organizations + + logging: + level: info + + # set connection timeouts for the peer and orderer for the client + connection: + timeout: + peer: + endorser: 3s + eventHub: 3s + eventReg: 3s + orderer: 3s + + # Needed to load users crypto keys and certs. + cryptoconfig: + path: path/to/cryptoconfig + + # enable/disable tls for the client + tls: + enabled: true + + # Some SDKs support pluggable KV stores, the properties under "credentialStore" + # are implementation specific + credentialStore: + # [Optional]. Not used by Go SDK. Others SDKs may use it if using an alternative impl + # Could be used if SDK would require an object for properties like url, db name, etc. + path: unused/by/sdk/go + + # [Optional]. Specific to the CryptoSuite implementation used by GO SDK. Software-based implementations + # requiring a key store. PKCS#11 based implementations does not. + cryptoStore: + # Specific to the underlying KeyValueStore that backs the crypto key store. + path: /usually/it/is/tmp/msp + + # [Optional]. Specific to Composer environment. Not used by SDK Go. + wallet: wallet-name-unused-by-sdk-go + + # BCCSP config for the client. Used by GO SDK. + BCCSP: + security: + enabled: true + default: + # provider: "SW" + provider: "" + # hashAlgorithm: "SHA2" + hashAlgorithm: "" + softVerify: true + ephemeral: false + level: 256 + pin: "somepin" + label: "ForFabric" + #library: "/usr/lib/x86_64-linux-gnu/softhsm/libsofthsm2.so, /usr/lib/softhsm/libsofthsm2.so ,/usr/lib/s390x-linux-gnu/softhsm/libsofthsm2.so, /usr/lib/powerpc64le-linux-gnu/softhsm/libsofthsm2.so, /usr/local/Cellar/softhsm/2.1.0/lib/softhsm/libsofthsm2.so" + library: "add BCCSP library here" + +# +# [Optional]. But most apps would have this section so that channel objects can be constructed +# based on the content below. If an app is creating channels, then it likely will not need this +# section. +# +channels: + # name of the channel +# mychannel: + # Required. list of orderers designated by the application to use for transactions on this + # channel. This list can be a result of access control ("org1" can only access "ordererA"), or + # operational decisions to share loads from applications among the orderers. The values must + # be "names" of orgs defined under "organizations/peers" +# orderers: +# - orderer.example.com + + # Required. list of peers from participating orgs +# peers: +# peer0.org1.example.com: + # [Optional]. will this peer be sent transaction proposals for endorsement? The peer must + # have the chaincode installed. The app can also use this property to decide which peers + # to send the chaincode install request. Default: true +# endorsingPeer: true + + # [Optional]. will this peer be sent query proposals? The peer must have the chaincode + # installed. The app can also use this property to decide which peers to send the + # chaincode install request. Default: true +# chaincodeQuery: true + + # [Optional]. will this peer be sent query proposals that do not require chaincodes, like + # queryBlock(), queryTransaction(), etc. Default: true +# ledgerQuery: true + + # [Optional]. will this peer be the target of the SDK's listener registration? All peers can + # produce events but the app typically only needs to connect to one to listen to events. + # Default: true +# eventSource: true + + + # [Optional]. what chaincodes are expected to exist on this channel? The application can use + # this information to validate that the target peers are in the expected state by comparing + # this list with the query results of getInstalledChaincodes() and getInstantiatedChaincodes() +# chaincodes: + # the format follows the "canonical name" of chaincodes by fabric code +# - example02:v1 +# - marbles:1.0 + +# +# list of participating organizations in this network +# +organizations: +# org1: +# mspid: Org1MSP + +# peers: +# - peer0.org1.example.com + + # [Optional]. Certificate Authorities issue certificates for identification purposes in a Fabric based + # network. Typically certificates provisioning is done in a separate process outside of the + # runtime network. Fabric-CA is a special certificate authority that provides a REST APIs for + # dynamic certificate management (enroll, revoke, re-enroll). The following section is only for + # Fabric-CA servers. +# certificateAuthorities: +# - ca-org1 + + # [Optional]. If the application is going to make requests that are reserved to organization + # administrators, including creating/updating channels, installing/instantiating chaincodes, it + # must have access to the admin identity represented by the private key and signing certificate. + # Both properties can be the PEM string or local path to the PEM file. Note that this is mainly for + # convenience in development mode, production systems should not expose sensitive information + # this way. The SDK should allow applications to set the org admin identity via APIs, and only use + # this route as an alternative when it exists. +# adminPrivateKey: +# pem: "-----BEGIN PRIVATE KEY----- " +# signedCert: +# path: "/tmp/somepath/signed-cert.pem" + + +# +# List of orderers to send transaction and channel create/update requests to. For the time +# being only one orderer is needed. If more than one is defined, which one get used by the +# SDK is implementation specific. Consult each SDK's documentation for its handling of orderers. +# +orderers: +# orderer.example.com: +# url: orderer.example.com:7050 + + # these are standard properties defined by the gRPC library + # they will be passed in as-is to gRPC client constructor +# grpcOptions: +# ssl-target-name-override: orderer.example.com +# grpc-max-send-message-length: 15 + +# tlsCACerts: + # Certificate location absolute path +# path: $GOPATH/src/github.com/hyperledger/fabric-sdk-go/test/fixtures/channel/crypto-config/ordererOrganizations/example.com/tlsca/tlsca.example.com-cert.pem + +# +# List of peers to send various requests to, including endorsement, query +# and event listener registration. +# +peers: +# peer0.org1.example.com: + # this URL is used to send endorsement and query requests +# url: peer0.org1.example.com:7051 + + # this URL is used to connect the EventHub and registering event listeners +# eventUrl: peer0.org1.example.com:7053 + +# grpcOptions: +# ssl-target-name-override: peer0.org1.example.com +# grpc.http2.keepalive_time: 15 + +# tlsCACerts: + # Certificate location absolute path +# path: path/to/tls/cert/for/peer0/org1 + +# +# Fabric-CA is a special kind of Certificate Authority provided by Hyperledger Fabric which allows +# certificate management to be done via REST APIs. Application may choose to use a standard +# Certificate Authority instead of Fabric-CA, in which case this section would not be specified. +# +certificateAuthorities: +# ca-org1: +# url: https://ca_peerOrg1:7054 + # the properties specified under this object are passed to the 'http' client verbatim when + # making the request to the Fabric-CA server +# httpOptions: +# verify: true +# tlsCACerts: + # Comma-Separated list of paths +# path: path/to/tls/cert/for/ca-org1 + # Client key and cert for SSL handshake with Fabric CA +# client: +# keyfile: path/to/client_fabric_client-key.pem +# certfile: path/to/client_fabric_client.pem + + # Fabric-CA supports dynamic user enrollment via REST APIs. A "root" user, a.k.a registrar, is + # needed to enroll and invoke new users. +# registrar: +# enrollId: usualy-it-is_admin +# enrollSecret: adminpasswd + # [Optional] The optional name of the CA. +# caName: ca-org1 diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 04dd403fb8..b00c4d639d 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -23,29 +23,19 @@ import ( var configImpl api.Config var org0 = "org0" var org1 = "org1" -var bccspProviderType string -var securityLevel = 256 - -const ( - providerTypeSW = "SW" -) - -var validRootCA = `-----BEGIN CERTIFICATE----- -MIICYjCCAgmgAwIBAgIUB3CTDOU47sUC5K4kn/Caqnh114YwCgYIKoZIzj0EAwIw -fzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh -biBGcmFuY2lzY28xHzAdBgNVBAoTFkludGVybmV0IFdpZGdldHMsIEluYy4xDDAK -BgNVBAsTA1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTYxMDEyMTkzMTAw -WhcNMjExMDExMTkzMTAwWjB/MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv -cm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEfMB0GA1UEChMWSW50ZXJuZXQg -V2lkZ2V0cywgSW5jLjEMMAoGA1UECxMDV1dXMRQwEgYDVQQDEwtleGFtcGxlLmNv -bTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKIH5b2JaSmqiQXHyqC+cmknICcF -i5AddVjsQizDV6uZ4v6s+PWiJyzfA/rTtMvYAPq/yeEHpBUB1j053mxnpMujYzBh -MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQXZ0I9 -qp6CP8TFHZ9bw5nRtZxIEDAfBgNVHSMEGDAWgBQXZ0I9qp6CP8TFHZ9bw5nRtZxI -EDAKBggqhkjOPQQDAgNHADBEAiAHp5Rbp9Em1G/UmKn8WsCbqDfWecVbZPQj3RK4 -oG5kQQIgQAe4OOKYhJdh3f7URaKfGTf492/nmRmtK+ySKjpHSrU= ------END CERTIFICATE-----` +func TestDefaultConfig(t *testing.T) { + vConfig := viper.New() + vConfig.AddConfigPath(".") + err := vConfig.ReadInConfig() + if err != nil { + t.Fatalf("Failed to load default config file") + } + //Test network name + if vConfig.GetString("name") != "default-network" { + t.Fatalf("Incorrect Network name from default config") + } +} func TestCAConfig(t *testing.T) { //Test config @@ -79,7 +69,7 @@ func TestCAConfig(t *testing.T) { } //Test client organization - if vConfig.GetString("client.organization") != "Org1" { + if vConfig.GetString("client.organization") != "org1" { t.Fatalf("Incorrect Client organization") } @@ -447,24 +437,26 @@ func TestPeerNotInOrgConfig(t *testing.T) { } } -func TestInitConfig(t *testing.T) { +func TestInitConfigSuccess(t *testing.T) { //Test init config //...Positive case _, err := InitConfig("../../test/fixtures/config/config_test.yaml") if err != nil { - t.Fatal("Failed to initialize config") - } - //...Negative case - _, err = InitConfig("invalid file location") - if err == nil { - t.Fatal("Config file initialization is supposed to fail") + t.Fatalf("Failed to initialize config. Error: %s", err) } +} - //Test init config with cmd root +func TestInitConfigWithCmdRoot(t *testing.T) { + TestInitConfigSuccess(t) + fileLoc := "../../test/fixtures/config/config_test.yaml" cmdRoot := "fabric_sdk" - _, err = InitConfigWithCmdRoot("../../test/fixtures/config/config_test.yaml", cmdRoot) + var logger = logging.MustGetLogger("config_test") + logger.Infof("fileLoc is %s", fileLoc) + + logger.Infof("fileLoc right before calling InitConfigWithCmdRoot is %s", fileLoc) + _, err := InitConfigWithCmdRoot(fileLoc, cmdRoot) if err != nil { - t.Fatal("Failed to initialize config with cmd root") + t.Fatalf("Failed to initialize config with cmd root. Error: %s", err) } //Test if Viper is initialized after calling init config @@ -490,6 +482,14 @@ func TestInitConfigPanic(t *testing.T) { InitConfigWithCmdRoot("../../test/fixtures/config/config_test.yaml", "fabric-sdk") } +func TestInitConfigInvalidLocation(t *testing.T) { + //...Negative case + _, err := InitConfig("invalid file location") + if err == nil { + t.Fatalf("Config file initialization is supposed to fail. Error: %s", err) + } +} + // Test case to create a new viper instance to prevent conflict with existing // viper instances in applications that use the SDK func TestMultipleVipers(t *testing.T) { @@ -586,13 +586,24 @@ func TestNetworkConfig(t *testing.T) { } func TestMain(m *testing.M) { + setUp(m) + r := m.Run() + teardown() + os.Exit(r) +} + +func setUp(m *testing.M) { + // do any test setup here... var err error configImpl, err = InitConfig("../../test/fixtures/config/config_test.yaml") if err != nil { fmt.Println(err.Error()) } +} - os.Exit(m.Run()) +func teardown() { + // do any teadown activities here .. + configImpl = nil } func crossCheckWithViperConfig(expected string, actual string, message string, t *testing.T) { diff --git a/test/fixtures/config/config_test.yaml b/test/fixtures/config/config_test.yaml index 777172976d..a6ab1a5466 100755 --- a/test/fixtures/config/config_test.yaml +++ b/test/fixtures/config/config_test.yaml @@ -37,7 +37,7 @@ version: 1.0.0 client: # Which organization does this application instance belong to? The value must be the name of an org # defined under "organizations" - organization: Org1 + organization: org1 logging: level: info @@ -141,7 +141,7 @@ channels: # list of participating organizations in this network # organizations: - Org1: + org1: mspid: Org1MSP peers: @@ -172,7 +172,7 @@ organizations: # peers with a public URL to send transaction proposals. The file will not contain private # information reserved for members of the organization, such as admin key and certificate, # fabric-ca registrar enroll ID and secret, etc. - Org2: + org2: mspid: Org2MSP peers: - peer0.org2.example.com diff --git a/test/integration/default_config_test.go b/test/integration/default_config_test.go new file mode 100644 index 0000000000..822e53783d --- /dev/null +++ b/test/integration/default_config_test.go @@ -0,0 +1,58 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package integration + +import ( + "os" + "testing" +) + +func TestDefaultConfig(t *testing.T) { + testSetup := &BaseSetupImpl{ + ConfigFile: "../../pkg/config/config.yaml", // explicitly set default config.yaml as setup() sets config_test.yaml for all tests + ChannelID: "mychannel", + OrgID: org1Name, + ChannelConfig: "../fixtures/channel/mychannel.tx", + ConnectEventHub: true, + } + + c, err := testSetup.InitConfig() + if err != nil { + t.Fatalf("Failed to load default config: %v", err) + } + n, err := c.NetworkConfig() + if err != nil { + t.Fatalf("Failed to load default network config: %v", err) + } + + if n.Name != "default-network" { + t.Fatalf("Default network was not loaded. Network name loaded is: %s", n.Name) + } +} + +func TestDefaultConfigFromEnvVariable(t *testing.T) { + testSetup := &BaseSetupImpl{ + ConfigFile: "../../pkg/config/config.yaml", // explicitly set default config.yaml as Setup test sets config_test.yaml for all tests + ChannelID: "mychannel", + OrgID: org1Name, + ChannelConfig: "../fixtures/channel/mychannel.tx", + ConnectEventHub: true, + } + // set env variable + os.Setenv("DEFAULT_SDK_CONFIG_PATH", "$GOPATH/src/github.com/hyperledger/fabric-sdk-go/pkg/config") + c, err := testSetup.InitConfig() + if err != nil { + t.Fatalf("Failed to load default config: %v", err) + } + n, err := c.NetworkConfig() + if err != nil { + t.Fatalf("Failed to load default network config: %v", err) + } + + if n.Name != "default-network" { + t.Fatalf("Default network was not loaded. Network name loaded is: %s", n.Name) + } +} diff --git a/test/integration/fabric_ca_test.go b/test/integration/fabric_ca_test.go index cd05caeb39..35593ab733 100644 --- a/test/integration/fabric_ca_test.go +++ b/test/integration/fabric_ca_test.go @@ -12,15 +12,12 @@ import ( "encoding/pem" "fmt" "math/rand" - "os" "strconv" "testing" "time" ca "github.com/hyperledger/fabric-sdk-go/api/apifabca" - config "github.com/hyperledger/fabric-sdk-go/api/apiconfig" - client "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client" "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/identity" kvs "github.com/hyperledger/fabric-sdk-go/pkg/fabric-client/keyvaluestore" @@ -31,40 +28,23 @@ import ( var org1Name = "org1" var org2Name = "org2" -var testFabricCAConfig config.Config - -func TestMain(m *testing.M) { - var err error - - testSetup := BaseSetupImpl{ - ConfigFile: ConfigTestFile, - } - - testFabricCAConfig, err = testSetup.InitConfig() - if err != nil { - fmt.Printf("Failed InitConfig [%s]\n", err) - os.Exit(1) - } - - os.Exit(m.Run()) -} // This test loads/enrols an admin user // Using the admin, it registers, enrols, and revokes a test user func TestRegisterEnrollRevoke(t *testing.T) { - mspID, err := testFabricCAConfig.MspID(org1Name) + mspID, err := testFabricConfig.MspID(org1Name) if err != nil { t.Fatalf("GetMspId() returned error: %v", err) } - caConfig, err := testFabricCAConfig.CAConfig(org1Name) + caConfig, err := testFabricConfig.CAConfig(org1Name) if err != nil { t.Fatalf("GetCAConfig returned error: %s", err) } - client := client.NewClient(testFabricCAConfig) + client := client.NewClient(testFabricConfig) - err = bccspFactory.InitFactories(testFabricCAConfig.CSPConfig()) + err = bccspFactory.InitFactories(testFabricConfig.CSPConfig()) if err != nil { t.Fatalf("Failed getting ephemeral software-based BCCSP [%s]", err) } @@ -78,7 +58,7 @@ func TestRegisterEnrollRevoke(t *testing.T) { } client.SetStateStore(stateStore) - caClient, err := fabricCAClient.NewFabricCAClient(testFabricCAConfig, org1Name) + caClient, err := fabricCAClient.NewFabricCAClient(testFabricConfig, org1Name) if err != nil { t.Fatalf("NewFabricCAClient return error: %v", err) } @@ -173,7 +153,7 @@ func TestRegisterEnrollRevoke(t *testing.T) { func TestEnrollOrg2(t *testing.T) { - caClient, err := fabricCAClient.NewFabricCAClient(testFabricCAConfig, org2Name) + caClient, err := fabricCAClient.NewFabricCAClient(testFabricConfig, org2Name) if err != nil { t.Fatalf("NewFabricCAClient return error: %v", err) } diff --git a/test/integration/main_test.go b/test/integration/main_test.go new file mode 100644 index 0000000000..110c57dfa0 --- /dev/null +++ b/test/integration/main_test.go @@ -0,0 +1,43 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package integration + +import ( + "fmt" + "os" + "testing" + + config "github.com/hyperledger/fabric-sdk-go/api/apiconfig" +) + +var testFabricConfig config.Config + +func TestMain(m *testing.M) { + setup() + r := m.Run() + teardown() + os.Exit(r) +} + +func setup() { + // do any test setup for all tests here... + var err error + + testSetup := BaseSetupImpl{ + ConfigFile: "../fixtures/config/config_test.yaml", + } + + testFabricConfig, err = testSetup.InitConfig() + if err != nil { + fmt.Printf("Failed InitConfig [%s]\n", err) + os.Exit(1) + } +} + +func teardown() { + // do any teadown activities here .. + testFabricConfig = nil +}