From 0404e72e61b5efdb45aba91870960e22b4408654 Mon Sep 17 00:00:00 2001 From: Sandra Vrtikapa Date: Tue, 1 May 2018 13:59:01 -0400 Subject: [PATCH] [FAB-9822] Resource Mgmt: Docs and Examples Change-Id: Ic7517835b03c1adc580bb030fe7fd84bf0c748c7 Signed-off-by: Sandra Vrtikapa --- pkg/client/resmgmt/example_test.go | 406 ++++++++++++++++++++ pkg/client/resmgmt/opts.go | 5 +- pkg/client/resmgmt/resmgmt.go | 104 ++++- test/integration/orgs/multiple_orgs_test.go | 2 +- test/integration/sdk/channel_client_test.go | 2 +- 5 files changed, 495 insertions(+), 24 deletions(-) create mode 100644 pkg/client/resmgmt/example_test.go diff --git a/pkg/client/resmgmt/example_test.go b/pkg/client/resmgmt/example_test.go new file mode 100644 index 0000000000..b4c20a0788 --- /dev/null +++ b/pkg/client/resmgmt/example_test.go @@ -0,0 +1,406 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package resmgmt + +import ( + "fmt" + "os" + "time" + + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/context" + "github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab" + sdkCtx "github.com/hyperledger/fabric-sdk-go/pkg/context" + "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" + "github.com/hyperledger/fabric-sdk-go/pkg/fab/resource/api" + mspmocks "github.com/hyperledger/fabric-sdk-go/pkg/msp/test/mockmsp" + "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/common/cauthdsl" + "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" +) + +func Example() { + + // Create new resource management client + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + // Read channel configuration + r, err := os.Open(channelConfig) + if err != nil { + fmt.Printf("failed to open channel config: %v", err) + } + defer r.Close() + + // Create new channel 'mychannel' + _, err = c.SaveChannel(SaveChannelRequest{ChannelID: "mychannel", ChannelConfig: r}) + if err != nil { + fmt.Printf("failed to save channel: %v", err) + } + + peer := mockPeer() + + // Peer joins channel 'mychannel' + err = c.JoinChannel("mychannel", WithTargets(peer)) + if err != nil { + fmt.Printf("failed to join channel: %v", err) + } + + // Install example chaincode to peer + installReq := InstallCCRequest{Name: "ExampleCC", Version: "v0", Path: "path", Package: &api.CCPackage{Type: 1, Code: []byte("bytes")}} + _, err = c.InstallCC(installReq, WithTargets(peer)) + if err != nil { + fmt.Printf("failed to install chaincode: %v", err) + } + + // Instantiate example chaincode on channel 'mychannel' + ccPolicy := cauthdsl.SignedByMspMember("Org1MSP") + instantiateReq := InstantiateCCRequest{Name: "ExampleCC", Version: "v0", Path: "path", Policy: ccPolicy} + _, err = c.InstantiateCC("mychannel", instantiateReq, WithTargets(peer)) + if err != nil { + fmt.Printf("failed to install chaincode: %v", err) + } + + fmt.Println("Network setup completed") + + // Output: Network setup completed +} + +func ExampleNew() { + + ctx := mockClientProvider() + + c, err := New(ctx) + if err != nil { + fmt.Println("failed to create client") + } + + if c != nil { + fmt.Println("resource management client created") + } + + // Output: resource management client created +} + +func ExampleWithDefaultTargetFilter() { + + ctx := mockClientProvider() + + c, err := New(ctx, WithDefaultTargetFilter(&urlTargetFilter{url: "example.com"})) + if err != nil { + fmt.Println("failed to create client") + } + + if c != nil { + fmt.Println("resource management client created with url target filter") + } + + // Output: resource management client created with url target filter +} + +// urlTargetFilter filters targets based on url +type urlTargetFilter struct { + url string +} + +// Accept returns true if this peer is to be included in the target list +func (f *urlTargetFilter) Accept(peer fab.Peer) bool { + return peer.URL() == f.url +} + +func ExampleWithParentContext() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + clientContext, err := mockClientProvider()() + if err != nil { + fmt.Println("failed to return client context") + return + } + + // get parent context and cancel + parentContext, cancel := sdkCtx.NewRequest(clientContext, sdkCtx.WithTimeout(20*time.Second)) + defer cancel() + + channels, err := c.QueryChannels(WithParentContext(parentContext), WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to query for blockchain info: %v", err) + } + + if channels != nil { + fmt.Println("Retrieved channels that peer belongs to") + } + + // Output: Retrieved channels that peer belongs to +} + +func ExampleWithTargets() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + response, err := c.QueryChannels(WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to query channels: %v", err) + } + + if response != nil { + fmt.Println("Retrieved channels") + } + + // Output: Retrieved channels +} + +func ExampleWithTargetFilter() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + ccPolicy := cauthdsl.SignedByMspMember("Org1MSP") + req := InstantiateCCRequest{Name: "ExampleCC", Version: "v0", Path: "path", Policy: ccPolicy} + + resp, err := c.InstantiateCC("mychannel", req, WithTargetFilter(&urlTargetFilter{url: "http://peer1.com"})) + if err != nil { + fmt.Printf("failed to install chaincode: %v", err) + } + + if resp.TransactionID == "" { + fmt.Println("Failed to instantiate chaincode") + } + + fmt.Println("Chaincode instantiated") + + // Output: Chaincode instantiated + +} + +func ExampleClient_SaveChannel() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Printf("failed to create client: %v", err) + } + + r, err := os.Open(channelConfig) + if err != nil { + fmt.Printf("failed to open channel config: %v", err) + } + defer r.Close() + + resp, err := c.SaveChannel(SaveChannelRequest{ChannelID: "mychannel", ChannelConfig: r}) + if err != nil { + fmt.Printf("failed to save channel: %v", err) + } + + if resp.TransactionID == "" { + fmt.Println("Failed to save channel") + } + + fmt.Println("Saved channel") + + // Output: Saved channel +} + +func ExampleClient_SaveChannel_withOrdererURL() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Printf("failed to create client: %v", err) + } + + r, err := os.Open(channelConfig) + if err != nil { + fmt.Printf("failed to open channel config: %v", err) + } + defer r.Close() + + resp, err := c.SaveChannel(SaveChannelRequest{ChannelID: "mychannel", ChannelConfig: r}, WithOrdererURL("example.com")) + if err != nil { + fmt.Printf("failed to save channel: %v", err) + } + + if resp.TransactionID == "" { + fmt.Println("Failed to save channel") + } + + fmt.Println("Saved channel") + + // Output: Saved channel + +} + +func ExampleClient_JoinChannel() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + err = c.JoinChannel("mychannel", WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to join channel: %v", err) + } + + fmt.Println("Joined channel") + + // Output: Joined channel +} + +func ExampleClient_InstallCC() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + req := InstallCCRequest{Name: "ExampleCC", Version: "v0", Path: "path", Package: &api.CCPackage{Type: 1, Code: []byte("bytes")}} + responses, err := c.InstallCC(req, WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to install chaincode: %v", err) + } + + if len(responses) > 0 { + fmt.Println("Chaincode installed") + } + + // Output: Chaincode installed +} + +func ExampleClient_InstantiateCC() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + ccPolicy := cauthdsl.SignedByMspMember("Org1MSP") + req := InstantiateCCRequest{Name: "ExampleCC", Version: "v0", Path: "path", Policy: ccPolicy} + + resp, err := c.InstantiateCC("mychannel", req) + if err != nil { + fmt.Printf("failed to install chaincode: %v", err) + } + + if resp.TransactionID == "" { + fmt.Println("Failed to instantiate chaincode") + } + + fmt.Println("Chaincode instantiated") + + // Output: Chaincode instantiated +} + +func ExampleClient_UpgradeCC() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + ccPolicy := cauthdsl.SignedByMspMember("Org1MSP") + req := UpgradeCCRequest{Name: "ExampleCC", Version: "v1", Path: "path", Policy: ccPolicy} + + resp, err := c.UpgradeCC("mychannel", req, WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to upgrade chaincode: %v", err) + } + + if resp.TransactionID == "" { + fmt.Println("Failed to upgrade chaincode") + } + + fmt.Println("Chaincode upgraded") + + // Output: Chaincode upgraded +} + +func ExampleClient_QueryChannels() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + response, err := c.QueryChannels(WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to query channels: %v", err) + } + + if response != nil { + fmt.Println("Retrieved channels") + } + + // Output: Retrieved channels +} + +func ExampleClient_QueryInstalledChaincodes() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + response, err := c.QueryInstalledChaincodes(WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to query installed chaincodes: %v", err) + } + + if response != nil { + fmt.Println("Retrieved installed chaincodes") + } + + // Output: Retrieved installed chaincodes +} + +func ExampleClient_QueryInstantiatedChaincodes() { + + c, err := New(mockClientProvider()) + if err != nil { + fmt.Println("failed to create client") + } + + response, err := c.QueryInstantiatedChaincodes("mychannel", WithTargets(mockPeer())) + if err != nil { + fmt.Printf("failed to query instantiated chaincodes: %v", err) + } + + if response != nil { + fmt.Println("Retrieved instantiated chaincodes") + } + + // Output: Retrieved instantiated chaincodes +} + +func mockClientProvider() context.ClientProvider { + + ctx := mocks.NewMockContext(mspmocks.NewMockSigningIdentity("test", "Org1MSP")) + + // Create mock orderer with simple mock block + orderer := mocks.NewMockOrderer("", nil) + defer orderer.Close() + + orderer.EnqueueForSendDeliver(mocks.NewSimpleMockBlock()) + orderer.EnqueueForSendDeliver(common.Status_SUCCESS) + + setupCustomOrderer(ctx, orderer) + + clientProvider := func() (context.Client, error) { + return ctx, nil + } + + return clientProvider +} + +func mockPeer() fab.Peer { + return &mocks.MockPeer{MockName: "Peer1", MockURL: "http://peer1.com", Status: 200} +} diff --git a/pkg/client/resmgmt/opts.go b/pkg/client/resmgmt/opts.go index 05b0d8d55a..e0162481ae 100644 --- a/pkg/client/resmgmt/opts.go +++ b/pkg/client/resmgmt/opts.go @@ -82,7 +82,6 @@ func WithTimeout(timeoutType fab.TimeoutType, timeout time.Duration) RequestOpti // WithOrdererURL allows an orderer to be specified for the request. // The orderer will be looked-up based on the url argument. -// A default orderer implementation will be used. func WithOrdererURL(url string) RequestOption { return func(ctx context.Client, opts *requestOptions) error { @@ -108,7 +107,7 @@ func WithOrderer(orderer fab.Orderer) RequestOption { } } -//WithParentContext encapsulates grpc context parent to Options +//WithParentContext encapsulates grpc parent context. func WithParentContext(parentContext reqContext.Context) RequestOption { return func(ctx context.Client, o *requestOptions) error { o.ParentContext = parentContext @@ -116,7 +115,7 @@ func WithParentContext(parentContext reqContext.Context) RequestOption { } } -// WithRetry sets retry options +// WithRetry sets retry options. func WithRetry(retryOpt retry.Opts) RequestOption { return func(ctx context.Client, o *requestOptions) error { o.Retry = retryOpt diff --git a/pkg/client/resmgmt/resmgmt.go b/pkg/client/resmgmt/resmgmt.go index 2114d1fab2..2389779086 100644 --- a/pkg/client/resmgmt/resmgmt.go +++ b/pkg/client/resmgmt/resmgmt.go @@ -4,7 +4,19 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -// Package resmgmt enables ability to update resources in a Fabric network. +// Package resmgmt enables creation and update of resources on a Fabric network. +// It allows administrators to create and/or update channnels, and for peers to join channels. +// Administrators can also perform chaincode related operations on a peer, such as +// installing, instantiating, and upgrading chaincode. +// +// Basic Flow: +// 1) Prepare client context +// 2) Create resource managememt client +// 3) Create new channel +// 4) Peer(s) join channel +// 5) Install chaincode onto peer(s) filesystem +// 6) Instantiate chaincode on channel +// 7) Query peer for channels, installed/instantiated chaincodes etc. package resmgmt import ( @@ -62,7 +74,7 @@ type InstantiateCCRequest struct { CollConfig []*common.CollectionConfig } -// InstantiateCCResponse contains response parameters for Instantiate +// InstantiateCCResponse contains response parameters for instantiate chaincode type InstantiateCCResponse struct { TransactionID fab.TransactionID } @@ -77,7 +89,7 @@ type UpgradeCCRequest struct { CollConfig []*common.CollectionConfig } -// UpgradeCCResponse contains response parameters for Upgrade +// UpgradeCCResponse contains response parameters for upgrade chaincode type UpgradeCCResponse struct { TransactionID fab.TransactionID } @@ -92,7 +104,7 @@ type requestOptions struct { Retry retry.Opts } -//SaveChannelRequest used to save channel request +//SaveChannelRequest holds parameters for save channel request type SaveChannelRequest struct { ChannelID string ChannelConfig io.Reader // ChannelConfig data source @@ -101,7 +113,7 @@ type SaveChannelRequest struct { // TODO: support pre-signed signature blocks } -// SaveChannelResponse contains response parameters for Save +// SaveChannelResponse contains response parameters for save channel type SaveChannelResponse struct { TransactionID fab.TransactionID } @@ -131,7 +143,7 @@ func (f *mspFilter) Accept(peer fab.Peer) bool { // ClientOption describes a functional parameter for the New constructor type ClientOption func(*Client) error -// WithDefaultTargetFilter option to configure new +// WithDefaultTargetFilter option to configure default target filter per client func WithDefaultTargetFilter(filter fab.TargetFilter) ClientOption { return func(rmc *Client) error { rmc.filter = filter @@ -139,7 +151,7 @@ func WithDefaultTargetFilter(filter fab.TargetFilter) ClientOption { } } -// New returns a ResourceMgmtClient instance +// New returns a resource management client instance. func New(ctxProvider context.ClientProvider, opts ...ClientOption) (*Client, error) { ctx, err := ctxProvider() @@ -175,7 +187,13 @@ func New(ctxProvider context.ClientProvider, opts ...ClientOption) (*Client, err return resourceClient, nil } -// JoinChannel allows for peers to join existing channel with optional custom options (specific peers, filtered peers) +// JoinChannel allows for peers to join existing channel with optional custom options (specific peers, filtered peers). If peer(s) are not specified in options it will default to all peers that belong to client's MSP. +// Parameters: +// channel is manadatory channel name +// options holds optional request options +// +// Returns: +// an error if join fails func (rc *Client) JoinChannel(channelID string, options ...RequestOption) error { if channelID == "" { @@ -332,7 +350,14 @@ func (rc *Client) isChaincodeInstalled(reqCtx reqContext.Context, req InstallCCR return false, nil } -// InstallCC installs chaincode with optional custom options (specific peers, filtered peers) +// InstallCC allows administrators to install chaincode onto the filesystem of a peer. +// If peer(s) are not specified in options it will default to all peers that belong to admin's MSP. +// Parameters: +// req holds info about mandatory chaincode name, path, version and policy +// options holds optional request options +// +// Returns: +// install chaincode proposal responses from peer(s) func (rc *Client) InstallCC(req InstallCCRequest, options ...RequestOption) ([]InstallCCResponse, error) { // For each peer query if chaincode installed. If cc is installed treat as success with message 'already installed'. // If cc is not installed try to install, and if that fails add to the list with error and peer name. @@ -445,7 +470,15 @@ func checkRequiredInstallCCParams(req InstallCCRequest) error { return nil } -// InstantiateCC instantiates chaincode using default settings +// InstantiateCC instantiates chaincode with optional custom options (specific peers, filtered peers, timeout). If peer(s) are not specified +// in options it will default to all channel peers. +// Parameters: +// channel is manadatory channel name +// req holds info about mandatory chaincode name, path, version and policy +// options holds optional request options +// +// Returns: +// instantiate chaincode response with transaction ID func (rc *Client) InstantiateCC(channelID string, req InstantiateCCRequest, options ...RequestOption) (InstantiateCCResponse, error) { opts, err := rc.prepareRequestOpts(options...) @@ -460,7 +493,15 @@ func (rc *Client) InstantiateCC(channelID string, req InstantiateCCRequest, opti return InstantiateCCResponse{TransactionID: txID}, err } -// UpgradeCC upgrades chaincode with optional custom options (specific peers, filtered peers, timeout) +// UpgradeCC upgrades chaincode with optional custom options (specific peers, filtered peers, timeout). If peer(s) are not specified in options +// it will default to all channel peers. +// Parameters: +// channel is manadatory channel name +// req holds info about mandatory chaincode name, path, version and policy +// options holds optional request options +// +// Returns: +// upgrade chaincode response with transaction ID func (rc *Client) UpgradeCC(channelID string, req UpgradeCCRequest, options ...RequestOption) (UpgradeCCResponse, error) { opts, err := rc.prepareRequestOpts(options...) @@ -476,7 +517,12 @@ func (rc *Client) UpgradeCC(channelID string, req UpgradeCCRequest, options ...R } // QueryInstalledChaincodes queries the installed chaincodes on a peer. -// Returns the details of all chaincodes installed on a peer. +// Parameters: +// options hold optional request options +// Note: One target(peer) has to be specified using either WithTargetURLs or WithTargets request option +// +// Returns: +// list of installed chaincodes on specified peer func (rc *Client) QueryInstalledChaincodes(options ...RequestOption) (*pb.ChaincodeQueryResponse, error) { opts, err := rc.prepareRequestOpts(options...) @@ -494,8 +540,13 @@ func (rc *Client) QueryInstalledChaincodes(options ...RequestOption) (*pb.Chainc return resource.QueryInstalledChaincodes(reqCtx, opts.Targets[0], resource.WithRetry(opts.Retry)) } -// QueryInstantiatedChaincodes queries the instantiated chaincodes on a peer for specific channel. -// Valid option is WithTarget. If not specified it will query any peer on this channel +// QueryInstantiatedChaincodes queries the instantiated chaincodes on a peer for specific channel. If peer is not specified in options it will query random peer on this channel. +// Parameters: +// channel is manadatory channel name +// options hold optional request options +// +// Returns: +// list of instantiated chaincodes func (rc *Client) QueryInstantiatedChaincodes(channelID string, options ...RequestOption) (*pb.ChaincodeQueryResponse, error) { opts, err := rc.prepareRequestOpts(options...) @@ -562,7 +613,12 @@ func (rc *Client) QueryInstantiatedChaincodes(channelID string, options ...Reque } // QueryChannels queries the names of all the channels that a peer has joined. -// Returns the details of all channels that peer has joined. +// Parameters: +// options hold optional request options +// Note: One target(peer) has to be specified using either WithTargetURLs or WithTargets request option +// +// Returns: +// all channels that peer has joined func (rc *Client) QueryChannels(options ...RequestOption) (*pb.ChannelQueryResponse, error) { opts, err := rc.prepareRequestOpts(options...) @@ -767,7 +823,13 @@ func peersToTxnProcessors(peers []fab.Peer) []fab.ProposalProcessor { return tpp } -// SaveChannel creates or updates channel +// SaveChannel creates or updates channel. +// Parameters: +// req holds info about mandatory channel name and configuration +// options holds optional request options +// +// Returns: +// save channel response with transaction ID func (rc *Client) SaveChannel(req SaveChannelRequest, options ...RequestOption) (SaveChannelResponse, error) { opts, err := rc.prepareRequestOpts(options...) @@ -881,9 +943,13 @@ func loggedClose(c io.Closer) { } } -// QueryConfigFromOrderer config returns channel configuration from orderer -// Valid request option is WithOrdererID -// If orderer id is not provided orderer will be defaulted to channel orderer (if configured) or random orderer from config +// QueryConfigFromOrderer config returns channel configuration from orderer. If orderer is not provided using options it will be defaulted to channel orderer (if configured) or random orderer from configuration. +// Parameters: +// channelID is mandatory channel ID +// options holds optional request options +// +// Returns: +// channel configuration func (rc *Client) QueryConfigFromOrderer(channelID string, options ...RequestOption) (fab.ChannelCfg, error) { opts, err := rc.prepareRequestOpts(options...) diff --git a/test/integration/orgs/multiple_orgs_test.go b/test/integration/orgs/multiple_orgs_test.go index 128bd75f12..02c7a68189 100644 --- a/test/integration/orgs/multiple_orgs_test.go +++ b/test/integration/orgs/multiple_orgs_test.go @@ -325,7 +325,7 @@ func queryCC(chClientOrg1User *channel.Client, t *testing.T) []byte { func verifyErrorFromCC(chClientOrg1User *channel.Client, t *testing.T) { r, err := chClientOrg1User.Query(channel.Request{ChaincodeID: "exampleCC", Fcn: "DUMMY_FUNCTION", Args: integration.ExampleCCQueryArgs()}, channel.WithRetry(retry.DefaultChannelOpts)) - t.Logf("verifyErrorFromCC err: %s ***** responses: %s", err, r) + t.Logf("verifyErrorFromCC err: %s ***** responses: %v", err, r) require.Error(t, err, "Should have failed with dummy function") s, ok := status.FromError(err) diff --git a/test/integration/sdk/channel_client_test.go b/test/integration/sdk/channel_client_test.go index 5171eaa423..59d9741cc1 100644 --- a/test/integration/sdk/channel_client_test.go +++ b/test/integration/sdk/channel_client_test.go @@ -299,7 +299,7 @@ func testChaincodeError(ccID string, client *channel.Client, t *testing.T) { r, err := client.Execute(channel.Request{ChaincodeID: ccID, Fcn: "DUMMY_FUNCTION", Args: integration.ExampleCCTxArgs()}, channel.WithRetry(retry.DefaultChannelOpts)) - t.Logf("testChaincodeError err: %s ***** responses: %s", err, r) + t.Logf("testChaincodeError err: %s ***** responses: %v", err, r) require.Error(t, err) s, ok := status.FromError(err) require.True(t, ok, "expected status error")