github.com/hyperledger-labs/bdls@v2.1.1+incompatible/integration/nwo/network.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package nwo 8 9 import ( 10 "bytes" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "os/exec" 16 "path/filepath" 17 "sort" 18 "strconv" 19 "strings" 20 "syscall" 21 "text/template" 22 "time" 23 24 docker "github.com/fsouza/go-dockerclient" 25 "github.com/hyperledger/fabric/integration/helpers" 26 "github.com/hyperledger/fabric/integration/nwo/commands" 27 "github.com/hyperledger/fabric/integration/nwo/fabricconfig" 28 "github.com/hyperledger/fabric/integration/runner" 29 "github.com/onsi/ginkgo" 30 . "github.com/onsi/gomega" 31 "github.com/onsi/gomega/gexec" 32 . "github.com/onsi/gomega/gstruct" 33 "github.com/onsi/gomega/matchers" 34 "github.com/onsi/gomega/types" 35 "github.com/tedsuo/ifrit" 36 "github.com/tedsuo/ifrit/ginkgomon" 37 "github.com/tedsuo/ifrit/grouper" 38 "gopkg.in/yaml.v2" 39 ) 40 41 // Organization models information about an Organization. It includes 42 // the information needed to populate an MSP with cryptogen. 43 type Organization struct { 44 MSPID string `yaml:"msp_id,omitempty"` 45 MSPType string `yaml:"msp_type,omitempty"` 46 Name string `yaml:"name,omitempty"` 47 Domain string `yaml:"domain,omitempty"` 48 EnableNodeOUs bool `yaml:"enable_node_organizational_units"` 49 Users int `yaml:"users,omitempty"` 50 CA *CA `yaml:"ca,omitempty"` 51 } 52 53 type CA struct { 54 Hostname string `yaml:"hostname,omitempty"` 55 } 56 57 // A Consortium is a named collection of Organizations. It is used to populate 58 // the Orderer geneesis block profile. 59 type Consortium struct { 60 Name string `yaml:"name,omitempty"` 61 Organizations []string `yaml:"organizations,omitempty"` 62 } 63 64 // Consensus indicates the orderer types and how many broker and zookeeper 65 // instances. 66 type Consensus struct { 67 Type string `yaml:"type,omitempty"` 68 Brokers int `yaml:"brokers,omitempty"` 69 ZooKeepers int `yaml:"zookeepers,omitempty"` 70 } 71 72 // The SystemChannel declares the name of the network system channel and its 73 // associated configtxgen profile name. 74 type SystemChannel struct { 75 Name string `yaml:"name,omitempty"` 76 Profile string `yaml:"profile,omitempty"` 77 } 78 79 // Channel associates a channel name with a configtxgen profile name. 80 type Channel struct { 81 Name string `yaml:"name,omitempty"` 82 Profile string `yaml:"profile,omitempty"` 83 BaseProfile string `yaml:"baseprofile,omitempty"` 84 } 85 86 // Orderer defines an orderer instance and its owning organization. 87 type Orderer struct { 88 Name string `yaml:"name,omitempty"` 89 Organization string `yaml:"organization,omitempty"` 90 } 91 92 // ID provides a unique identifier for an orderer instance. 93 func (o Orderer) ID() string { 94 return fmt.Sprintf("%s.%s", o.Organization, o.Name) 95 } 96 97 // Peer defines a peer instance, it's owning organization, and the list of 98 // channels that the peer should be joined to. 99 type Peer struct { 100 Name string `yaml:"name,omitempty"` 101 Organization string `yaml:"organization,omitempty"` 102 Channels []*PeerChannel `yaml:"channels,omitempty"` 103 } 104 105 // PeerChannel names of the channel a peer should be joined to and whether or 106 // not the peer should be an anchor for the channel. 107 type PeerChannel struct { 108 Name string `yaml:"name,omitempty"` 109 Anchor bool `yaml:"anchor"` 110 } 111 112 // ID provides a unique identifier for a peer instance. 113 func (p *Peer) ID() string { 114 return fmt.Sprintf("%s.%s", p.Organization, p.Name) 115 } 116 117 // Anchor returns true if this peer is an anchor for any channel it has joined. 118 func (p *Peer) Anchor() bool { 119 for _, c := range p.Channels { 120 if c.Anchor { 121 return true 122 } 123 } 124 return false 125 } 126 127 // A profile encapsulates basic information for a configtxgen profile. 128 type Profile struct { 129 Name string `yaml:"name,omitempty"` 130 Orderers []string `yaml:"orderers,omitempty"` 131 Consortium string `yaml:"consortium,omitempty"` 132 Organizations []string `yaml:"organizations,omitempty"` 133 AppCapabilities []string `yaml:"app_capabilities,omitempty"` 134 ChannelCapabilities []string `yaml:"channel_capabilities,omitempty"` 135 } 136 137 // Network holds information about a fabric network. 138 type Network struct { 139 RootDir string 140 StartPort uint16 141 Components *Components 142 DockerClient *docker.Client 143 ExternalBuilders []fabricconfig.ExternalBuilder 144 NetworkID string 145 EventuallyTimeout time.Duration 146 MetricsProvider string 147 StatsdEndpoint string 148 ClientAuthRequired bool 149 150 PortsByBrokerID map[string]Ports 151 PortsByOrdererID map[string]Ports 152 PortsByPeerID map[string]Ports 153 Organizations []*Organization 154 SystemChannel *SystemChannel 155 Channels []*Channel 156 Consensus *Consensus 157 Orderers []*Orderer 158 Peers []*Peer 159 Profiles []*Profile 160 Consortiums []*Consortium 161 Templates *Templates 162 163 colorIndex uint 164 } 165 166 // New creates a Network from a simple configuration. All generated or managed 167 // artifacts for the network will be located under rootDir. Ports will be 168 // allocated sequentially from the specified startPort. 169 func New(c *Config, rootDir string, client *docker.Client, startPort int, components *Components) *Network { 170 network := &Network{ 171 StartPort: uint16(startPort), 172 RootDir: rootDir, 173 Components: components, 174 DockerClient: client, 175 176 NetworkID: helpers.UniqueName(), 177 EventuallyTimeout: time.Minute, 178 MetricsProvider: "prometheus", 179 PortsByBrokerID: map[string]Ports{}, 180 PortsByOrdererID: map[string]Ports{}, 181 PortsByPeerID: map[string]Ports{}, 182 183 Organizations: c.Organizations, 184 Consensus: c.Consensus, 185 Orderers: c.Orderers, 186 Peers: c.Peers, 187 SystemChannel: c.SystemChannel, 188 Channels: c.Channels, 189 Profiles: c.Profiles, 190 Consortiums: c.Consortiums, 191 Templates: c.Templates, 192 } 193 194 cwd, err := os.Getwd() 195 Expect(err).NotTo(HaveOccurred()) 196 network.ExternalBuilders = []fabricconfig.ExternalBuilder{{ 197 Path: filepath.Join(cwd, "..", "externalbuilders", "binary"), 198 Name: "binary", 199 EnvironmentWhitelist: []string{"GOPROXY"}, 200 }} 201 202 if network.Templates == nil { 203 network.Templates = &Templates{} 204 } 205 206 for i := 0; i < network.Consensus.Brokers; i++ { 207 ports := Ports{} 208 for _, portName := range BrokerPortNames() { 209 ports[portName] = network.ReservePort() 210 } 211 network.PortsByBrokerID[strconv.Itoa(i)] = ports 212 } 213 214 for _, o := range c.Orderers { 215 ports := Ports{} 216 for _, portName := range OrdererPortNames() { 217 ports[portName] = network.ReservePort() 218 } 219 network.PortsByOrdererID[o.ID()] = ports 220 } 221 222 for _, p := range c.Peers { 223 ports := Ports{} 224 for _, portName := range PeerPortNames() { 225 ports[portName] = network.ReservePort() 226 } 227 network.PortsByPeerID[p.ID()] = ports 228 } 229 return network 230 } 231 232 // AddOrg adds an organization to a network. 233 func (n *Network) AddOrg(o *Organization, peers ...*Peer) { 234 for _, p := range peers { 235 ports := Ports{} 236 for _, portName := range PeerPortNames() { 237 ports[portName] = n.ReservePort() 238 } 239 n.PortsByPeerID[p.ID()] = ports 240 n.Peers = append(n.Peers, p) 241 } 242 243 n.Organizations = append(n.Organizations, o) 244 n.Consortiums[0].Organizations = append(n.Consortiums[0].Organizations, o.Name) 245 } 246 247 // ConfigTxPath returns the path to the generated configtxgen configuration 248 // file. 249 func (n *Network) ConfigTxConfigPath() string { 250 return filepath.Join(n.RootDir, "configtx.yaml") 251 } 252 253 // CryptoPath returns the path to the directory where cryptogen will place its 254 // generated artifacts. 255 func (n *Network) CryptoPath() string { 256 return filepath.Join(n.RootDir, "crypto") 257 } 258 259 // CryptoConfigPath returns the path to the generated cryptogen configuration 260 // file. 261 func (n *Network) CryptoConfigPath() string { 262 return filepath.Join(n.RootDir, "crypto-config.yaml") 263 } 264 265 // OutputBlockPath returns the path to the genesis block for the named system 266 // channel. 267 func (n *Network) OutputBlockPath(channelName string) string { 268 return filepath.Join(n.RootDir, fmt.Sprintf("%s_block.pb", channelName)) 269 } 270 271 // CreateChannelTxPath returns the path to the create channel transaction for 272 // the named channel. 273 func (n *Network) CreateChannelTxPath(channelName string) string { 274 return filepath.Join(n.RootDir, fmt.Sprintf("%s_tx.pb", channelName)) 275 } 276 277 // OrdererDir returns the path to the configuration directory for the specified 278 // Orderer. 279 func (n *Network) OrdererDir(o *Orderer) string { 280 return filepath.Join(n.RootDir, "orderers", o.ID()) 281 } 282 283 // OrdererConfigPath returns the path to the orderer configuration document for 284 // the specified Orderer. 285 func (n *Network) OrdererConfigPath(o *Orderer) string { 286 return filepath.Join(n.OrdererDir(o), "orderer.yaml") 287 } 288 289 // ReadOrdererConfig unmarshals an orderer's orderer.yaml and returns an 290 // object approximating its contents. 291 func (n *Network) ReadOrdererConfig(o *Orderer) *fabricconfig.Orderer { 292 var orderer fabricconfig.Orderer 293 ordererBytes, err := ioutil.ReadFile(n.OrdererConfigPath(o)) 294 Expect(err).NotTo(HaveOccurred()) 295 296 err = yaml.Unmarshal(ordererBytes, &orderer) 297 Expect(err).NotTo(HaveOccurred()) 298 299 return &orderer 300 } 301 302 // WriteOrdererConfig serializes the provided configuration as the specified 303 // orderer's orderer.yaml document. 304 func (n *Network) WriteOrdererConfig(o *Orderer, config *fabricconfig.Orderer) { 305 ordererBytes, err := yaml.Marshal(config) 306 Expect(err).NotTo(HaveOccurred()) 307 308 err = ioutil.WriteFile(n.OrdererConfigPath(o), ordererBytes, 0644) 309 Expect(err).NotTo(HaveOccurred()) 310 } 311 312 // ReadConfigTxConfig unmarshals the configtx.yaml and returns an 313 // object approximating its contents. 314 func (n *Network) ReadConfigTxConfig() *fabricconfig.ConfigTx { 315 var configtx fabricconfig.ConfigTx 316 configtxBytes, err := ioutil.ReadFile(n.ConfigTxConfigPath()) 317 Expect(err).NotTo(HaveOccurred()) 318 319 err = yaml.Unmarshal(configtxBytes, &configtx) 320 Expect(err).NotTo(HaveOccurred()) 321 322 return &configtx 323 } 324 325 // WriteConfigTxConfig serializes the provided configuration to configtx.yaml. 326 func (n *Network) WriteConfigTxConfig(config *fabricconfig.ConfigTx) { 327 configtxBytes, err := yaml.Marshal(config) 328 Expect(err).NotTo(HaveOccurred()) 329 330 err = ioutil.WriteFile(n.ConfigTxConfigPath(), configtxBytes, 0644) 331 Expect(err).NotTo(HaveOccurred()) 332 } 333 334 // PeerDir returns the path to the configuration directory for the specified 335 // Peer. 336 func (n *Network) PeerDir(p *Peer) string { 337 return filepath.Join(n.RootDir, "peers", p.ID()) 338 } 339 340 // PeerConfigPath returns the path to the peer configuration document for the 341 // specified peer. 342 func (n *Network) PeerConfigPath(p *Peer) string { 343 return filepath.Join(n.PeerDir(p), "core.yaml") 344 } 345 346 // PeerLedgerDir returns the ledger root directory for the specified peer. 347 func (n *Network) PeerLedgerDir(p *Peer) string { 348 return filepath.Join(n.PeerDir(p), "filesystem/ledgersData") 349 } 350 351 // ReadPeerConfig unmarshals a peer's core.yaml and returns an object 352 // approximating its contents. 353 func (n *Network) ReadPeerConfig(p *Peer) *fabricconfig.Core { 354 var core fabricconfig.Core 355 coreBytes, err := ioutil.ReadFile(n.PeerConfigPath(p)) 356 Expect(err).NotTo(HaveOccurred()) 357 358 err = yaml.Unmarshal(coreBytes, &core) 359 Expect(err).NotTo(HaveOccurred()) 360 361 return &core 362 } 363 364 // WritePeerConfig serializes the provided configuration as the specified 365 // peer's core.yaml document. 366 func (n *Network) WritePeerConfig(p *Peer, config *fabricconfig.Core) { 367 coreBytes, err := yaml.Marshal(config) 368 Expect(err).NotTo(HaveOccurred()) 369 370 err = ioutil.WriteFile(n.PeerConfigPath(p), coreBytes, 0644) 371 Expect(err).NotTo(HaveOccurred()) 372 } 373 374 // peerUserCryptoDir returns the path to the directory containing the 375 // certificates and keys for the specified user of the peer. 376 func (n *Network) peerUserCryptoDir(p *Peer, user, cryptoMaterialType string) string { 377 org := n.Organization(p.Organization) 378 Expect(org).NotTo(BeNil()) 379 380 return n.userCryptoDir(org, "peerOrganizations", user, cryptoMaterialType) 381 } 382 383 // ordererUserCryptoDir returns the path to the directory containing the 384 // certificates and keys for the specified user of the orderer. 385 func (n *Network) ordererUserCryptoDir(o *Orderer, user, cryptoMaterialType string) string { 386 org := n.Organization(o.Organization) 387 Expect(org).NotTo(BeNil()) 388 389 return n.userCryptoDir(org, "ordererOrganizations", user, cryptoMaterialType) 390 } 391 392 // userCryptoDir returns the path to the folder with crypto materials for either peers or orderer organizations 393 // specific user 394 func (n *Network) userCryptoDir(org *Organization, nodeOrganizationType, user, cryptoMaterialType string) string { 395 return filepath.Join( 396 n.RootDir, 397 "crypto", 398 nodeOrganizationType, 399 org.Domain, 400 "users", 401 fmt.Sprintf("%s@%s", user, org.Domain), 402 cryptoMaterialType, 403 ) 404 } 405 406 // PeerUserMSPDir returns the path to the MSP directory containing the 407 // certificates and keys for the specified user of the peer. 408 func (n *Network) PeerUserMSPDir(p *Peer, user string) string { 409 return n.peerUserCryptoDir(p, user, "msp") 410 } 411 412 // IdemixUserMSPDir returns the path to the MSP directory containing the 413 // idemix-related crypto material for the specified user of the organization. 414 func (n *Network) IdemixUserMSPDir(o *Organization, user string) string { 415 return n.userCryptoDir(o, "peerOrganizations", user, "") 416 } 417 418 // OrdererUserMSPDir returns the path to the MSP directory containing the 419 // certificates and keys for the specified user of the peer. 420 func (n *Network) OrdererUserMSPDir(o *Orderer, user string) string { 421 return n.ordererUserCryptoDir(o, user, "msp") 422 } 423 424 // PeerUserTLSDir returns the path to the TLS directory containing the 425 // certificates and keys for the specified user of the peer. 426 func (n *Network) PeerUserTLSDir(p *Peer, user string) string { 427 return n.peerUserCryptoDir(p, user, "tls") 428 } 429 430 // PeerUserCert returns the path to the certificate for the specified user in 431 // the peer organization. 432 func (n *Network) PeerUserCert(p *Peer, user string) string { 433 org := n.Organization(p.Organization) 434 Expect(org).NotTo(BeNil()) 435 436 return filepath.Join( 437 n.PeerUserMSPDir(p, user), 438 "signcerts", 439 fmt.Sprintf("%s@%s-cert.pem", user, org.Domain), 440 ) 441 } 442 443 // OrdererUserCert returns the path to the certificate for the specified user in 444 // the orderer organization. 445 func (n *Network) OrdererUserCert(o *Orderer, user string) string { 446 org := n.Organization(o.Organization) 447 Expect(org).NotTo(BeNil()) 448 449 return filepath.Join( 450 n.OrdererUserMSPDir(o, user), 451 "signcerts", 452 fmt.Sprintf("%s@%s-cert.pem", user, org.Domain), 453 ) 454 } 455 456 // PeerUserKey returns the path to the private key for the specified user in 457 // the peer organization. 458 func (n *Network) PeerUserKey(p *Peer, user string) string { 459 org := n.Organization(p.Organization) 460 Expect(org).NotTo(BeNil()) 461 462 keystore := filepath.Join( 463 n.PeerUserMSPDir(p, user), 464 "keystore", 465 ) 466 467 // file names are the SKI and non-deterministic 468 keys, err := ioutil.ReadDir(keystore) 469 Expect(err).NotTo(HaveOccurred()) 470 Expect(keys).To(HaveLen(1)) 471 472 return filepath.Join(keystore, keys[0].Name()) 473 } 474 475 // OrdererUserKey returns the path to the private key for the specified user in 476 // the orderer organization. 477 func (n *Network) OrdererUserKey(o *Orderer, user string) string { 478 org := n.Organization(o.Organization) 479 Expect(org).NotTo(BeNil()) 480 481 keystore := filepath.Join( 482 n.OrdererUserMSPDir(o, user), 483 "keystore", 484 ) 485 486 // file names are the SKI and non-deterministic 487 keys, err := ioutil.ReadDir(keystore) 488 Expect(err).NotTo(HaveOccurred()) 489 Expect(keys).To(HaveLen(1)) 490 491 return filepath.Join(keystore, keys[0].Name()) 492 } 493 494 // peerLocalCryptoDir returns the path to the local crypto directory for the peer. 495 func (n *Network) peerLocalCryptoDir(p *Peer, cryptoType string) string { 496 org := n.Organization(p.Organization) 497 Expect(org).NotTo(BeNil()) 498 499 return filepath.Join( 500 n.RootDir, 501 "crypto", 502 "peerOrganizations", 503 org.Domain, 504 "peers", 505 fmt.Sprintf("%s.%s", p.Name, org.Domain), 506 cryptoType, 507 ) 508 } 509 510 // PeerLocalMSPDir returns the path to the local MSP directory for the peer. 511 func (n *Network) PeerLocalMSPDir(p *Peer) string { 512 return n.peerLocalCryptoDir(p, "msp") 513 } 514 515 // PeerLocalTLSDir returns the path to the local TLS directory for the peer. 516 func (n *Network) PeerLocalTLSDir(p *Peer) string { 517 return n.peerLocalCryptoDir(p, "tls") 518 } 519 520 // PeerCert returns the path to the peer's certificate. 521 func (n *Network) PeerCert(p *Peer) string { 522 org := n.Organization(p.Organization) 523 Expect(org).NotTo(BeNil()) 524 525 return filepath.Join( 526 n.PeerLocalMSPDir(p), 527 "signcerts", 528 fmt.Sprintf("%s.%s-cert.pem", p.Name, org.Domain), 529 ) 530 } 531 532 // PeerOrgMSPDir returns the path to the MSP directory of the Peer organization. 533 func (n *Network) PeerOrgMSPDir(org *Organization) string { 534 return filepath.Join( 535 n.RootDir, 536 "crypto", 537 "peerOrganizations", 538 org.Domain, 539 "msp", 540 ) 541 } 542 543 func (n *Network) IdemixOrgMSPDir(org *Organization) string { 544 return filepath.Join( 545 n.RootDir, 546 "crypto", 547 "peerOrganizations", 548 org.Domain, 549 ) 550 } 551 552 // OrdererOrgMSPDir returns the path to the MSP directory of the Orderer 553 // organization. 554 func (n *Network) OrdererOrgMSPDir(o *Organization) string { 555 return filepath.Join( 556 n.RootDir, 557 "crypto", 558 "ordererOrganizations", 559 o.Domain, 560 "msp", 561 ) 562 } 563 564 // OrdererLocalCryptoDir returns the path to the local crypto directory for the 565 // Orderer. 566 func (n *Network) OrdererLocalCryptoDir(o *Orderer, cryptoType string) string { 567 org := n.Organization(o.Organization) 568 Expect(org).NotTo(BeNil()) 569 570 return filepath.Join( 571 n.RootDir, 572 "crypto", 573 "ordererOrganizations", 574 org.Domain, 575 "orderers", 576 fmt.Sprintf("%s.%s", o.Name, org.Domain), 577 cryptoType, 578 ) 579 } 580 581 // OrdererLocalMSPDir returns the path to the local MSP directory for the 582 // Orderer. 583 func (n *Network) OrdererLocalMSPDir(o *Orderer) string { 584 return n.OrdererLocalCryptoDir(o, "msp") 585 } 586 587 // OrdererLocalTLSDir returns the path to the local TLS directory for the 588 // Orderer. 589 func (n *Network) OrdererLocalTLSDir(o *Orderer) string { 590 return n.OrdererLocalCryptoDir(o, "tls") 591 } 592 593 // ProfileForChannel gets the configtxgen profile name associated with the 594 // specified channel. 595 func (n *Network) ProfileForChannel(channelName string) string { 596 for _, ch := range n.Channels { 597 if ch.Name == channelName { 598 return ch.Profile 599 } 600 } 601 return "" 602 } 603 604 // CACertsBundlePath returns the path to the bundle of CA certificates for the 605 // network. This bundle is used when connecting to peers. 606 func (n *Network) CACertsBundlePath() string { 607 return filepath.Join( 608 n.RootDir, 609 "crypto", 610 "ca-certs.pem", 611 ) 612 } 613 614 // GenerateConfigTree generates the configuration documents required to 615 // bootstrap a fabric network. A configuration file will be generated for 616 // cryptogen, configtxgen, and for each peer and orderer. The contents of the 617 // documents will be based on the Config used to create the Network. 618 // 619 // When this method completes, the resulting tree will look something like 620 // this: 621 // 622 // ${rootDir}/configtx.yaml 623 // ${rootDir}/crypto-config.yaml 624 // ${rootDir}/orderers/orderer0.orderer-org/orderer.yaml 625 // ${rootDir}/peers/peer0.org1/core.yaml 626 // ${rootDir}/peers/peer0.org2/core.yaml 627 // ${rootDir}/peers/peer1.org1/core.yaml 628 // ${rootDir}/peers/peer1.org2/core.yaml 629 // 630 func (n *Network) GenerateConfigTree() { 631 n.GenerateCryptoConfig() 632 n.GenerateConfigTxConfig() 633 for _, o := range n.Orderers { 634 n.GenerateOrdererConfig(o) 635 } 636 for _, p := range n.Peers { 637 n.GenerateCoreConfig(p) 638 } 639 } 640 641 // Bootstrap generates the cryptographic material, orderer system channel 642 // genesis block, and create channel transactions needed to run a fabric 643 // network. 644 // 645 // The cryptogen tool is used to create crypto material from the contents of 646 // ${rootDir}/crypto-config.yaml. The generated artifacts will be placed in 647 // ${rootDir}/crypto/... 648 // 649 // The gensis block is generated from the profile referenced by the 650 // SystemChannel.Profile attribute. The block is written to 651 // ${rootDir}/${SystemChannel.Name}_block.pb. 652 // 653 // The create channel transactions are generated for each Channel referenced by 654 // the Network using the channel's Profile attribute. The transactions are 655 // written to ${rootDir}/${Channel.Name}_tx.pb. 656 func (n *Network) Bootstrap() { 657 if n.DockerClient != nil { 658 _, err := n.DockerClient.CreateNetwork( 659 docker.CreateNetworkOptions{ 660 Name: n.NetworkID, 661 Driver: "bridge", 662 }, 663 ) 664 Expect(err).NotTo(HaveOccurred()) 665 } 666 667 sess, err := n.Cryptogen(commands.Generate{ 668 Config: n.CryptoConfigPath(), 669 Output: n.CryptoPath(), 670 }) 671 Expect(err).NotTo(HaveOccurred()) 672 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 673 674 n.bootstrapIdemix() 675 676 sess, err = n.ConfigTxGen(commands.OutputBlock{ 677 ChannelID: n.SystemChannel.Name, 678 Profile: n.SystemChannel.Profile, 679 ConfigPath: n.RootDir, 680 OutputBlock: n.OutputBlockPath(n.SystemChannel.Name), 681 }) 682 Expect(err).NotTo(HaveOccurred()) 683 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 684 685 for _, c := range n.Channels { 686 sess, err := n.ConfigTxGen(commands.CreateChannelTx{ 687 ChannelID: c.Name, 688 Profile: c.Profile, 689 BaseProfile: c.BaseProfile, 690 ConfigPath: n.RootDir, 691 OutputCreateChannelTx: n.CreateChannelTxPath(c.Name), 692 }) 693 Expect(err).NotTo(HaveOccurred()) 694 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 695 } 696 697 n.ConcatenateTLSCACertificates() 698 } 699 700 // bootstrapIdemix creates the idemix-related crypto material 701 func (n *Network) bootstrapIdemix() { 702 for j, org := range n.IdemixOrgs() { 703 704 output := n.IdemixOrgMSPDir(org) 705 // - ca-keygen 706 sess, err := n.Idemixgen(commands.CAKeyGen{ 707 Output: output, 708 }) 709 Expect(err).NotTo(HaveOccurred()) 710 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 711 712 // - signerconfig 713 usersOutput := filepath.Join(n.IdemixOrgMSPDir(org), "users") 714 userOutput := filepath.Join(usersOutput, fmt.Sprintf("User%d@%s", 1, org.Domain)) 715 sess, err = n.Idemixgen(commands.SignerConfig{ 716 CAInput: output, 717 Output: userOutput, 718 OrgUnit: org.Domain, 719 EnrollmentID: "User" + string(1), 720 RevocationHandle: fmt.Sprintf("1%d%d", 1, j), 721 }) 722 Expect(err).NotTo(HaveOccurred()) 723 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 724 } 725 } 726 727 // ConcatenateTLSCACertificates concatenates all TLS CA certificates into a 728 // single file to be used by peer CLI. 729 func (n *Network) ConcatenateTLSCACertificates() { 730 bundle := &bytes.Buffer{} 731 for _, tlsCertPath := range n.listTLSCACertificates() { 732 certBytes, err := ioutil.ReadFile(tlsCertPath) 733 Expect(err).NotTo(HaveOccurred()) 734 bundle.Write(certBytes) 735 } 736 err := ioutil.WriteFile(n.CACertsBundlePath(), bundle.Bytes(), 0660) 737 Expect(err).NotTo(HaveOccurred()) 738 } 739 740 // listTLSCACertificates returns the paths of all TLS CA certificates in the 741 // network, across all organizations. 742 func (n *Network) listTLSCACertificates() []string { 743 fileName2Path := make(map[string]string) 744 filepath.Walk(filepath.Join(n.RootDir, "crypto"), func(path string, info os.FileInfo, err error) error { 745 // File starts with "tlsca" and has "-cert.pem" in it 746 if strings.HasPrefix(info.Name(), "tlsca") && strings.Contains(info.Name(), "-cert.pem") { 747 fileName2Path[info.Name()] = path 748 } 749 return nil 750 }) 751 752 var tlsCACertificates []string 753 for _, path := range fileName2Path { 754 tlsCACertificates = append(tlsCACertificates, path) 755 } 756 return tlsCACertificates 757 } 758 759 // Cleanup attempts to cleanup docker related artifacts that may 760 // have been created by the network. 761 func (n *Network) Cleanup() { 762 if n.DockerClient == nil { 763 return 764 } 765 766 nw, err := n.DockerClient.NetworkInfo(n.NetworkID) 767 Expect(err).NotTo(HaveOccurred()) 768 769 err = n.DockerClient.RemoveNetwork(nw.ID) 770 Expect(err).NotTo(HaveOccurred()) 771 772 containers, err := n.DockerClient.ListContainers(docker.ListContainersOptions{All: true}) 773 Expect(err).NotTo(HaveOccurred()) 774 for _, c := range containers { 775 for _, name := range c.Names { 776 if strings.HasPrefix(name, "/"+n.NetworkID) { 777 err := n.DockerClient.RemoveContainer(docker.RemoveContainerOptions{ID: c.ID, Force: true}) 778 Expect(err).NotTo(HaveOccurred()) 779 break 780 } 781 } 782 } 783 784 images, err := n.DockerClient.ListImages(docker.ListImagesOptions{All: true}) 785 Expect(err).NotTo(HaveOccurred()) 786 for _, i := range images { 787 for _, tag := range i.RepoTags { 788 if strings.HasPrefix(tag, n.NetworkID) { 789 err := n.DockerClient.RemoveImage(i.ID) 790 Expect(err).NotTo(HaveOccurred()) 791 break 792 } 793 } 794 } 795 } 796 797 // CreateAndJoinChannels will create all channels specified in the config that 798 // are referenced by peers. The referencing peers will then be joined to the 799 // channel(s). 800 // 801 // The network must be running before this is called. 802 func (n *Network) CreateAndJoinChannels(o *Orderer) { 803 for _, c := range n.Channels { 804 n.CreateAndJoinChannel(o, c.Name) 805 } 806 } 807 808 // CreateAndJoinChannel will create the specified channel. The referencing 809 // peers will then be joined to the channel. 810 // 811 // The network must be running before this is called. 812 func (n *Network) CreateAndJoinChannel(o *Orderer, channelName string) { 813 peers := n.PeersWithChannel(channelName) 814 if len(peers) == 0 { 815 return 816 } 817 818 n.CreateChannel(channelName, o, peers[0]) 819 n.JoinChannel(channelName, o, peers...) 820 } 821 822 // UpdateChannelAnchors determines the anchor peers for the specified channel, 823 // creates an anchor peer update transaction for each organization, and submits 824 // the update transactions to the orderer. 825 func (n *Network) UpdateChannelAnchors(o *Orderer, channelName string) { 826 tempFile, err := ioutil.TempFile("", "update-anchors") 827 Expect(err).NotTo(HaveOccurred()) 828 tempFile.Close() 829 defer os.Remove(tempFile.Name()) 830 831 peersByOrg := map[string]*Peer{} 832 for _, p := range n.AnchorsForChannel(channelName) { 833 peersByOrg[p.Organization] = p 834 } 835 836 for orgName, p := range peersByOrg { 837 anchorUpdate := commands.OutputAnchorPeersUpdate{ 838 OutputAnchorPeersUpdate: tempFile.Name(), 839 ChannelID: channelName, 840 Profile: n.ProfileForChannel(channelName), 841 ConfigPath: n.RootDir, 842 AsOrg: orgName, 843 } 844 sess, err := n.ConfigTxGen(anchorUpdate) 845 Expect(err).NotTo(HaveOccurred()) 846 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 847 848 sess, err = n.PeerAdminSession(p, commands.ChannelUpdate{ 849 ChannelID: channelName, 850 Orderer: n.OrdererAddress(o, ListenPort), 851 File: tempFile.Name(), 852 ClientAuth: n.ClientAuthRequired, 853 }) 854 Expect(err).NotTo(HaveOccurred()) 855 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 856 } 857 } 858 859 // VerifyMembership checks that each peer has discovered the expected 860 // peers in the network 861 func (n *Network) VerifyMembership(expectedPeers []*Peer, channel string, chaincodes ...string) { 862 // all peers currently include _lifecycle as an available chaincode 863 chaincodes = append(chaincodes, "_lifecycle") 864 expectedDiscoveredPeerMatchers := make([]types.GomegaMatcher, len(expectedPeers)) 865 for i, peer := range expectedPeers { 866 expectedDiscoveredPeerMatchers[i] = n.DiscoveredPeerMatcher(peer, chaincodes...) //n.DiscoveredPeer(peer, chaincodes...) 867 } 868 for _, peer := range expectedPeers { 869 Eventually(DiscoverPeers(n, peer, "User1", channel), n.EventuallyTimeout).Should(ConsistOf(expectedDiscoveredPeerMatchers)) 870 } 871 } 872 873 // CreateChannel will submit an existing create channel transaction to the 874 // specified orderer. The channel transaction must exist at the location 875 // returned by CreateChannelTxPath. Optionally, additional signers may be 876 // included in the case where the channel creation tx modifies other 877 // aspects of the channel config for the new channel. 878 // 879 // The orderer must be running when this is called. 880 func (n *Network) CreateChannel(channelName string, o *Orderer, p *Peer, additionalSigners ...interface{}) { 881 channelCreateTxPath := n.CreateChannelTxPath(channelName) 882 n.signConfigTransaction(channelCreateTxPath, p, additionalSigners...) 883 884 createChannel := func() int { 885 sess, err := n.PeerAdminSession(p, commands.ChannelCreate{ 886 ChannelID: channelName, 887 Orderer: n.OrdererAddress(o, ListenPort), 888 File: channelCreateTxPath, 889 OutputBlock: "/dev/null", 890 ClientAuth: n.ClientAuthRequired, 891 }) 892 Expect(err).NotTo(HaveOccurred()) 893 return sess.Wait(n.EventuallyTimeout).ExitCode() 894 } 895 Eventually(createChannel, n.EventuallyTimeout).Should(Equal(0)) 896 } 897 898 // CreateChannelExitCode will submit an existing create channel transaction to 899 // the specified orderer, wait for the operation to complete, and return the 900 // exit status of the "peer channel create" command. 901 // 902 // The channel transaction must exist at the location returned by 903 // CreateChannelTxPath and the orderer must be running when this is called. 904 func (n *Network) CreateChannelExitCode(channelName string, o *Orderer, p *Peer, additionalSigners ...interface{}) int { 905 channelCreateTxPath := n.CreateChannelTxPath(channelName) 906 n.signConfigTransaction(channelCreateTxPath, p, additionalSigners...) 907 908 sess, err := n.PeerAdminSession(p, commands.ChannelCreate{ 909 ChannelID: channelName, 910 Orderer: n.OrdererAddress(o, ListenPort), 911 File: channelCreateTxPath, 912 OutputBlock: "/dev/null", 913 ClientAuth: n.ClientAuthRequired, 914 }) 915 Expect(err).NotTo(HaveOccurred()) 916 return sess.Wait(n.EventuallyTimeout).ExitCode() 917 } 918 919 func (n *Network) signConfigTransaction(channelTxPath string, submittingPeer *Peer, signers ...interface{}) { 920 for _, signer := range signers { 921 switch signer := signer.(type) { 922 case *Peer: 923 sess, err := n.PeerAdminSession(signer, commands.SignConfigTx{ 924 File: channelTxPath, 925 ClientAuth: n.ClientAuthRequired, 926 }) 927 Expect(err).NotTo(HaveOccurred()) 928 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 929 930 case *Orderer: 931 sess, err := n.OrdererAdminSession(signer, submittingPeer, commands.SignConfigTx{ 932 File: channelTxPath, 933 ClientAuth: n.ClientAuthRequired, 934 }) 935 Expect(err).NotTo(HaveOccurred()) 936 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 937 938 default: 939 panic(fmt.Sprintf("unknown signer type %T, expect Peer or Orderer", signer)) 940 } 941 } 942 } 943 944 // JoinChannel will join peers to the specified channel. The orderer is used to 945 // obtain the current configuration block for the channel. 946 // 947 // The orderer and listed peers must be running before this is called. 948 func (n *Network) JoinChannel(name string, o *Orderer, peers ...*Peer) { 949 if len(peers) == 0 { 950 return 951 } 952 953 tempFile, err := ioutil.TempFile("", "genesis-block") 954 Expect(err).NotTo(HaveOccurred()) 955 tempFile.Close() 956 defer os.Remove(tempFile.Name()) 957 958 sess, err := n.PeerAdminSession(peers[0], commands.ChannelFetch{ 959 Block: "0", 960 ChannelID: name, 961 Orderer: n.OrdererAddress(o, ListenPort), 962 OutputFile: tempFile.Name(), 963 ClientAuth: n.ClientAuthRequired, 964 }) 965 Expect(err).NotTo(HaveOccurred()) 966 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 967 968 for _, p := range peers { 969 sess, err := n.PeerAdminSession(p, commands.ChannelJoin{ 970 BlockPath: tempFile.Name(), 971 ClientAuth: n.ClientAuthRequired, 972 }) 973 Expect(err).NotTo(HaveOccurred()) 974 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 975 } 976 } 977 978 // Cryptogen starts a gexec.Session for the provided cryptogen command. 979 func (n *Network) Cryptogen(command Command) (*gexec.Session, error) { 980 cmd := NewCommand(n.Components.Cryptogen(), command) 981 return n.StartSession(cmd, command.SessionName()) 982 } 983 984 // Idemixgen starts a gexec.Session for the provided idemixgen command. 985 func (n *Network) Idemixgen(command Command) (*gexec.Session, error) { 986 cmd := NewCommand(n.Components.Idemixgen(), command) 987 return n.StartSession(cmd, command.SessionName()) 988 } 989 990 // ConfigTxGen starts a gexec.Session for the provided configtxgen command. 991 func (n *Network) ConfigTxGen(command Command) (*gexec.Session, error) { 992 cmd := NewCommand(n.Components.ConfigTxGen(), command) 993 return n.StartSession(cmd, command.SessionName()) 994 } 995 996 // Discover starts a gexec.Session for the provided discover command. 997 func (n *Network) Discover(command Command) (*gexec.Session, error) { 998 cmd := NewCommand(n.Components.Discover(), command) 999 cmd.Args = append(cmd.Args, "--peerTLSCA", n.CACertsBundlePath()) 1000 return n.StartSession(cmd, command.SessionName()) 1001 } 1002 1003 // ZooKeeperRunner returns a runner for a ZooKeeper instance. 1004 func (n *Network) ZooKeeperRunner(idx int) *runner.ZooKeeper { 1005 colorCode := n.nextColor() 1006 name := fmt.Sprintf("zookeeper-%d-%s", idx, n.NetworkID) 1007 1008 return &runner.ZooKeeper{ 1009 ZooMyID: idx + 1, // IDs must be between 1 and 255 1010 Client: n.DockerClient, 1011 Name: name, 1012 NetworkName: n.NetworkID, 1013 OutputStream: gexec.NewPrefixedWriter( 1014 fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1015 ginkgo.GinkgoWriter, 1016 ), 1017 ErrorStream: gexec.NewPrefixedWriter( 1018 fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1019 ginkgo.GinkgoWriter, 1020 ), 1021 } 1022 } 1023 1024 func (n *Network) minBrokersInSync() int { 1025 if n.Consensus.Brokers < 2 { 1026 return n.Consensus.Brokers 1027 } 1028 return 2 1029 } 1030 1031 func (n *Network) defaultBrokerReplication() int { 1032 if n.Consensus.Brokers < 3 { 1033 return n.Consensus.Brokers 1034 } 1035 return 3 1036 } 1037 1038 // BrokerRunner returns a runner for an kafka broker instance. 1039 func (n *Network) BrokerRunner(id int, zookeepers []string) *runner.Kafka { 1040 colorCode := n.nextColor() 1041 name := fmt.Sprintf("kafka-%d-%s", id, n.NetworkID) 1042 1043 return &runner.Kafka{ 1044 BrokerID: id + 1, 1045 Client: n.DockerClient, 1046 AdvertisedListeners: "127.0.0.1", 1047 HostPort: int(n.PortsByBrokerID[strconv.Itoa(id)][HostPort]), 1048 Name: name, 1049 NetworkName: n.NetworkID, 1050 MinInsyncReplicas: n.minBrokersInSync(), 1051 DefaultReplicationFactor: n.defaultBrokerReplication(), 1052 ZooKeeperConnect: strings.Join(zookeepers, ","), 1053 OutputStream: gexec.NewPrefixedWriter( 1054 fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1055 ginkgo.GinkgoWriter, 1056 ), 1057 ErrorStream: gexec.NewPrefixedWriter( 1058 fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1059 ginkgo.GinkgoWriter, 1060 ), 1061 } 1062 } 1063 1064 // BrokerGroupRunner returns a runner that manages the processes that make up 1065 // the kafka broker network for fabric. 1066 func (n *Network) BrokerGroupRunner() ifrit.Runner { 1067 members := grouper.Members{} 1068 zookeepers := []string{} 1069 1070 for i := 0; i < n.Consensus.ZooKeepers; i++ { 1071 zk := n.ZooKeeperRunner(i) 1072 zookeepers = append(zookeepers, fmt.Sprintf("%s:2181", zk.Name)) 1073 members = append(members, grouper.Member{Name: zk.Name, Runner: zk}) 1074 } 1075 1076 for i := 0; i < n.Consensus.Brokers; i++ { 1077 kafka := n.BrokerRunner(i, zookeepers) 1078 members = append(members, grouper.Member{Name: kafka.Name, Runner: kafka}) 1079 } 1080 1081 return grouper.NewOrdered(syscall.SIGTERM, members) 1082 } 1083 1084 // OrdererRunner returns an ifrit.Runner for the specified orderer. The runner 1085 // can be used to start and manage an orderer process. 1086 func (n *Network) OrdererRunner(o *Orderer) *ginkgomon.Runner { 1087 cmd := exec.Command(n.Components.Orderer()) 1088 cmd.Env = os.Environ() 1089 cmd.Env = append(cmd.Env, fmt.Sprintf("FABRIC_CFG_PATH=%s", n.OrdererDir(o))) 1090 1091 config := ginkgomon.Config{ 1092 AnsiColorCode: n.nextColor(), 1093 Name: o.ID(), 1094 Command: cmd, 1095 StartCheck: "Beginning to serve requests", 1096 StartCheckTimeout: 15 * time.Second, 1097 } 1098 1099 // After consensus-type migration, the #brokers is >0, but the type is etcdraft 1100 if n.Consensus.Type == "kafka" && n.Consensus.Brokers != 0 { 1101 config.StartCheck = "Start phase completed successfully" 1102 config.StartCheckTimeout = 30 * time.Second 1103 } 1104 1105 return ginkgomon.New(config) 1106 } 1107 1108 // OrdererGroupRunner returns a runner that can be used to start and stop all 1109 // orderers in a network. 1110 func (n *Network) OrdererGroupRunner() ifrit.Runner { 1111 members := grouper.Members{} 1112 for _, o := range n.Orderers { 1113 members = append(members, grouper.Member{Name: o.ID(), Runner: n.OrdererRunner(o)}) 1114 } 1115 return grouper.NewParallel(syscall.SIGTERM, members) 1116 } 1117 1118 // PeerRunner returns an ifrit.Runner for the specified peer. The runner can be 1119 // used to start and manage a peer process. 1120 func (n *Network) PeerRunner(p *Peer, env ...string) *ginkgomon.Runner { 1121 cmd := n.peerCommand( 1122 commands.NodeStart{PeerID: p.ID()}, 1123 "", 1124 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1125 ) 1126 cmd.Env = append(cmd.Env, env...) 1127 1128 return ginkgomon.New(ginkgomon.Config{ 1129 AnsiColorCode: n.nextColor(), 1130 Name: p.ID(), 1131 Command: cmd, 1132 StartCheck: `Started peer with ID=.*, .*, address=`, 1133 StartCheckTimeout: 15 * time.Second, 1134 }) 1135 } 1136 1137 // PeerGroupRunner returns a runner that can be used to start and stop all 1138 // peers in a network. 1139 func (n *Network) PeerGroupRunner() ifrit.Runner { 1140 members := grouper.Members{} 1141 for _, p := range n.Peers { 1142 members = append(members, grouper.Member{Name: p.ID(), Runner: n.PeerRunner(p)}) 1143 } 1144 return grouper.NewParallel(syscall.SIGTERM, members) 1145 } 1146 1147 // NetworkGroupRunner returns a runner that can be used to start and stop an 1148 // entire fabric network. 1149 func (n *Network) NetworkGroupRunner() ifrit.Runner { 1150 members := grouper.Members{ 1151 {Name: "brokers", Runner: n.BrokerGroupRunner()}, 1152 {Name: "orderers", Runner: n.OrdererGroupRunner()}, 1153 {Name: "peers", Runner: n.PeerGroupRunner()}, 1154 } 1155 return grouper.NewOrdered(syscall.SIGTERM, members) 1156 } 1157 1158 func (n *Network) peerCommand(command Command, tlsDir string, env ...string) *exec.Cmd { 1159 cmd := NewCommand(n.Components.Peer(), command) 1160 cmd.Env = append(cmd.Env, env...) 1161 if ConnectsToOrderer(command) { 1162 cmd.Args = append(cmd.Args, "--tls") 1163 cmd.Args = append(cmd.Args, "--cafile", n.CACertsBundlePath()) 1164 } 1165 1166 if ClientAuthEnabled(command) { 1167 certfilePath := filepath.Join(tlsDir, "client.crt") 1168 keyfilePath := filepath.Join(tlsDir, "client.key") 1169 1170 cmd.Args = append(cmd.Args, "--certfile", certfilePath) 1171 cmd.Args = append(cmd.Args, "--keyfile", keyfilePath) 1172 } 1173 1174 // In case we have a peer invoke with multiple certificates, 1175 // we need to mimic the correct peer CLI usage, 1176 // so we count the number of --peerAddresses usages 1177 // we have, and add the same (concatenated TLS CA certificates file) 1178 // the same number of times to bypass the peer CLI sanity checks 1179 requiredPeerAddresses := flagCount("--peerAddresses", cmd.Args) 1180 for i := 0; i < requiredPeerAddresses; i++ { 1181 cmd.Args = append(cmd.Args, "--tlsRootCertFiles") 1182 cmd.Args = append(cmd.Args, n.CACertsBundlePath()) 1183 } 1184 return cmd 1185 } 1186 1187 func flagCount(flag string, args []string) int { 1188 var c int 1189 for _, arg := range args { 1190 if arg == flag { 1191 c++ 1192 } 1193 } 1194 return c 1195 } 1196 1197 // PeerAdminSession starts a gexec.Session as a peer admin for the provided 1198 // peer command. This is intended to be used by short running peer cli commands 1199 // that execute in the context of a peer configuration. 1200 func (n *Network) PeerAdminSession(p *Peer, command Command) (*gexec.Session, error) { 1201 return n.PeerUserSession(p, "Admin", command) 1202 } 1203 1204 // PeerUserSession starts a gexec.Session as a peer user for the provided peer 1205 // command. This is intended to be used by short running peer cli commands that 1206 // execute in the context of a peer configuration. 1207 func (n *Network) PeerUserSession(p *Peer, user string, command Command) (*gexec.Session, error) { 1208 cmd := n.peerCommand( 1209 command, 1210 n.PeerUserTLSDir(p, user), 1211 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1212 fmt.Sprintf("CORE_PEER_MSPCONFIGPATH=%s", n.PeerUserMSPDir(p, user)), 1213 ) 1214 return n.StartSession(cmd, command.SessionName()) 1215 } 1216 1217 // IdemixUserSession starts a gexec.Session as a idemix user for the provided peer 1218 // command. This is intended to be used by short running peer cli commands that 1219 // execute in the context of a peer configuration. 1220 func (n *Network) IdemixUserSession(p *Peer, idemixOrg *Organization, user string, command Command) (*gexec.Session, error) { 1221 cmd := n.peerCommand( 1222 command, 1223 n.PeerUserTLSDir(p, user), 1224 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1225 fmt.Sprintf("CORE_PEER_MSPCONFIGPATH=%s", n.IdemixUserMSPDir(idemixOrg, user)), 1226 fmt.Sprintf("CORE_PEER_LOCALMSPTYPE=%s", "idemix"), 1227 fmt.Sprintf("CORE_PEER_LOCALMSPID=%s", idemixOrg.MSPID), 1228 ) 1229 return n.StartSession(cmd, command.SessionName()) 1230 } 1231 1232 // OrdererAdminSession starts a gexec.Session as an orderer admin user. This 1233 // is used primarily to generate orderer configuration updates. 1234 func (n *Network) OrdererAdminSession(o *Orderer, p *Peer, command Command) (*gexec.Session, error) { 1235 cmd := n.peerCommand( 1236 command, 1237 n.ordererUserCryptoDir(o, "Admin", "tls"), 1238 fmt.Sprintf("CORE_PEER_LOCALMSPID=%s", n.Organization(o.Organization).MSPID), 1239 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1240 fmt.Sprintf("CORE_PEER_MSPCONFIGPATH=%s", n.OrdererUserMSPDir(o, "Admin")), 1241 ) 1242 return n.StartSession(cmd, command.SessionName()) 1243 } 1244 1245 // Peer returns the information about the named Peer in the named organization. 1246 func (n *Network) Peer(orgName, peerName string) *Peer { 1247 for _, p := range n.PeersInOrg(orgName) { 1248 if p.Name == peerName { 1249 return p 1250 } 1251 } 1252 return nil 1253 } 1254 1255 // DiscoveredPeer creates a new DiscoveredPeer from the peer and chaincodes 1256 // passed as arguments. 1257 func (n *Network) DiscoveredPeer(p *Peer, chaincodes ...string) DiscoveredPeer { 1258 peerCert, err := ioutil.ReadFile(n.PeerCert(p)) 1259 Expect(err).NotTo(HaveOccurred()) 1260 1261 return DiscoveredPeer{ 1262 MSPID: n.Organization(p.Organization).MSPID, 1263 Endpoint: fmt.Sprintf("127.0.0.1:%d", n.PeerPort(p, ListenPort)), 1264 Identity: string(peerCert), 1265 Chaincodes: chaincodes, 1266 } 1267 } 1268 1269 func (n *Network) DiscoveredPeerMatcher(p *Peer, chaincodes ...string) types.GomegaMatcher { 1270 peerCert, err := ioutil.ReadFile(n.PeerCert(p)) 1271 Expect(err).NotTo(HaveOccurred()) 1272 1273 return MatchAllFields(Fields{ 1274 "MSPID": Equal(n.Organization(p.Organization).MSPID), 1275 "Endpoint": Equal(fmt.Sprintf("127.0.0.1:%d", n.PeerPort(p, ListenPort))), 1276 "Identity": Equal(string(peerCert)), 1277 "Chaincodes": containElements(chaincodes...), 1278 }) 1279 } 1280 1281 // containElements succeeds if a slice contains the passed in elements. 1282 func containElements(elements ...string) types.GomegaMatcher { 1283 ms := make([]types.GomegaMatcher, 0, len(elements)) 1284 for _, element := range elements { 1285 ms = append(ms, &matchers.ContainElementMatcher{ 1286 Element: element, 1287 }) 1288 } 1289 return &matchers.AndMatcher{ 1290 Matchers: ms, 1291 } 1292 } 1293 1294 // Orderer returns the information about the named Orderer. 1295 func (n *Network) Orderer(name string) *Orderer { 1296 for _, o := range n.Orderers { 1297 if o.Name == name { 1298 return o 1299 } 1300 } 1301 return nil 1302 } 1303 1304 // Organization returns the information about the named Organization. 1305 func (n *Network) Organization(orgName string) *Organization { 1306 for _, org := range n.Organizations { 1307 if org.Name == orgName { 1308 return org 1309 } 1310 } 1311 return nil 1312 } 1313 1314 // Consortium returns information about the named Consortium. 1315 func (n *Network) Consortium(name string) *Consortium { 1316 for _, c := range n.Consortiums { 1317 if c.Name == name { 1318 return c 1319 } 1320 } 1321 return nil 1322 } 1323 1324 // PeerOrgs returns all Organizations associated with at least one Peer. 1325 func (n *Network) PeerOrgs() []*Organization { 1326 orgsByName := map[string]*Organization{} 1327 for _, p := range n.Peers { 1328 if n.Organization(p.Organization).MSPType != "idemix" { 1329 orgsByName[p.Organization] = n.Organization(p.Organization) 1330 } 1331 } 1332 1333 orgs := []*Organization{} 1334 for _, org := range orgsByName { 1335 orgs = append(orgs, org) 1336 } 1337 return orgs 1338 } 1339 1340 // IdemixOrgs returns all Organizations of type idemix. 1341 func (n *Network) IdemixOrgs() []*Organization { 1342 orgs := []*Organization{} 1343 for _, org := range n.Organizations { 1344 if org.MSPType == "idemix" { 1345 orgs = append(orgs, org) 1346 } 1347 } 1348 return orgs 1349 } 1350 1351 // PeersWithChannel returns all Peer instances that have joined the named 1352 // channel. 1353 func (n *Network) PeersWithChannel(chanName string) []*Peer { 1354 peers := []*Peer{} 1355 for _, p := range n.Peers { 1356 for _, c := range p.Channels { 1357 if c.Name == chanName { 1358 peers = append(peers, p) 1359 } 1360 } 1361 } 1362 1363 // This is a bit of a hack to make the output of this function deterministic. 1364 // When this function's output is supplied as input to functions such as ApproveChaincodeForMyOrg 1365 // it causes a different subset of peers to be picked, which can create flakiness in tests. 1366 sort.Slice(peers, func(i, j int) bool { 1367 if peers[i].Organization < peers[j].Organization { 1368 return true 1369 } 1370 1371 return peers[i].Organization == peers[j].Organization && peers[i].Name < peers[j].Name 1372 }) 1373 return peers 1374 } 1375 1376 // AnchorsForChannel returns all Peer instances that are anchors for the 1377 // named channel. 1378 func (n *Network) AnchorsForChannel(chanName string) []*Peer { 1379 anchors := []*Peer{} 1380 for _, p := range n.Peers { 1381 for _, pc := range p.Channels { 1382 if pc.Name == chanName && pc.Anchor { 1383 anchors = append(anchors, p) 1384 } 1385 } 1386 } 1387 return anchors 1388 } 1389 1390 // AnchorsInOrg returns all peers that are an anchor for at least one channel 1391 // in the named organization. 1392 func (n *Network) AnchorsInOrg(orgName string) []*Peer { 1393 anchors := []*Peer{} 1394 for _, p := range n.PeersInOrg(orgName) { 1395 if p.Anchor() { 1396 anchors = append(anchors, p) 1397 break 1398 } 1399 } 1400 1401 // No explicit anchor means all peers are anchors. 1402 if len(anchors) == 0 { 1403 anchors = n.PeersInOrg(orgName) 1404 } 1405 1406 return anchors 1407 } 1408 1409 // OrderersInOrg returns all Orderer instances owned by the named organaiztion. 1410 func (n *Network) OrderersInOrg(orgName string) []*Orderer { 1411 orderers := []*Orderer{} 1412 for _, o := range n.Orderers { 1413 if o.Organization == orgName { 1414 orderers = append(orderers, o) 1415 } 1416 } 1417 return orderers 1418 } 1419 1420 // OrgsForOrderers returns all Organization instances that own at least one of 1421 // the named orderers. 1422 func (n *Network) OrgsForOrderers(ordererNames []string) []*Organization { 1423 orgsByName := map[string]*Organization{} 1424 for _, name := range ordererNames { 1425 orgName := n.Orderer(name).Organization 1426 orgsByName[orgName] = n.Organization(orgName) 1427 } 1428 orgs := []*Organization{} 1429 for _, org := range orgsByName { 1430 orgs = append(orgs, org) 1431 } 1432 return orgs 1433 } 1434 1435 // OrdererOrgs returns all Organization instances that own at least one 1436 // orderer. 1437 func (n *Network) OrdererOrgs() []*Organization { 1438 orgsByName := map[string]*Organization{} 1439 for _, o := range n.Orderers { 1440 orgsByName[o.Organization] = n.Organization(o.Organization) 1441 } 1442 1443 orgs := []*Organization{} 1444 for _, org := range orgsByName { 1445 orgs = append(orgs, org) 1446 } 1447 return orgs 1448 } 1449 1450 // PeersInOrg returns all Peer instances that are owned by the named 1451 // organization. 1452 func (n *Network) PeersInOrg(orgName string) []*Peer { 1453 peers := []*Peer{} 1454 for _, o := range n.Peers { 1455 if o.Organization == orgName { 1456 peers = append(peers, o) 1457 } 1458 } 1459 return peers 1460 } 1461 1462 // ReservePort allocates the next available port. 1463 func (n *Network) ReservePort() uint16 { 1464 n.StartPort++ 1465 return n.StartPort - 1 1466 } 1467 1468 type PortName string 1469 type Ports map[PortName]uint16 1470 1471 const ( 1472 ChaincodePort PortName = "Chaincode" 1473 EventsPort PortName = "Events" 1474 HostPort PortName = "HostPort" 1475 ListenPort PortName = "Listen" 1476 ProfilePort PortName = "Profile" 1477 OperationsPort PortName = "Operations" 1478 ClusterPort PortName = "Cluster" 1479 ) 1480 1481 // PeerPortNames returns the list of ports that need to be reserved for a Peer. 1482 func PeerPortNames() []PortName { 1483 return []PortName{ListenPort, ChaincodePort, EventsPort, ProfilePort, OperationsPort} 1484 } 1485 1486 // OrdererPortNames returns the list of ports that need to be reserved for an 1487 // Orderer. 1488 func OrdererPortNames() []PortName { 1489 return []PortName{ListenPort, ProfilePort, OperationsPort, ClusterPort} 1490 } 1491 1492 // BrokerPortNames returns the list of ports that need to be reserved for a 1493 // Kafka broker. 1494 func BrokerPortNames() []PortName { 1495 return []PortName{HostPort} 1496 } 1497 1498 // BrokerAddresses returns the list of broker addresses for the network. 1499 func (n *Network) BrokerAddresses(portName PortName) []string { 1500 addresses := []string{} 1501 for _, ports := range n.PortsByBrokerID { 1502 addresses = append(addresses, fmt.Sprintf("127.0.0.1:%d", ports[portName])) 1503 } 1504 return addresses 1505 } 1506 1507 // OrdererAddress returns the address (host and port) exposed by the Orderer 1508 // for the named port. Command line tools should use the returned address when 1509 // connecting to the orderer. 1510 // 1511 // This assumes that the orderer is listening on 0.0.0.0 or 127.0.0.1 and is 1512 // available on the loopback address. 1513 func (n *Network) OrdererAddress(o *Orderer, portName PortName) string { 1514 return fmt.Sprintf("127.0.0.1:%d", n.OrdererPort(o, portName)) 1515 } 1516 1517 // OrdererPort returns the named port reserved for the Orderer instance. 1518 func (n *Network) OrdererPort(o *Orderer, portName PortName) uint16 { 1519 ordererPorts := n.PortsByOrdererID[o.ID()] 1520 Expect(ordererPorts).NotTo(BeNil()) 1521 return ordererPorts[portName] 1522 } 1523 1524 // PeerAddress returns the address (host and port) exposed by the Peer for the 1525 // named port. Command line tools should use the returned address when 1526 // connecting to a peer. 1527 // 1528 // This assumes that the peer is listening on 0.0.0.0 and is available on the 1529 // loopback address. 1530 func (n *Network) PeerAddress(p *Peer, portName PortName) string { 1531 return fmt.Sprintf("127.0.0.1:%d", n.PeerPort(p, portName)) 1532 } 1533 1534 // PeerPort returns the named port reserved for the Peer instance. 1535 func (n *Network) PeerPort(p *Peer, portName PortName) uint16 { 1536 peerPorts := n.PortsByPeerID[p.ID()] 1537 Expect(peerPorts).NotTo(BeNil()) 1538 return peerPorts[portName] 1539 } 1540 1541 func (n *Network) nextColor() string { 1542 color := n.colorIndex%14 + 31 1543 if color > 37 { 1544 color = color + 90 - 37 1545 } 1546 1547 n.colorIndex++ 1548 return fmt.Sprintf("%dm", color) 1549 } 1550 1551 // StartSession executes a command session. This should be used to launch 1552 // command line tools that are expected to run to completion. 1553 func (n *Network) StartSession(cmd *exec.Cmd, name string) (*gexec.Session, error) { 1554 ansiColorCode := n.nextColor() 1555 fmt.Fprintf( 1556 ginkgo.GinkgoWriter, 1557 "\x1b[33m[d]\x1b[%s[%s]\x1b[0m starting %s %s\n", 1558 ansiColorCode, 1559 name, 1560 filepath.Base(cmd.Args[0]), 1561 strings.Join(cmd.Args[1:], " "), 1562 ) 1563 return gexec.Start( 1564 cmd, 1565 gexec.NewPrefixedWriter( 1566 fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", ansiColorCode, name), 1567 ginkgo.GinkgoWriter, 1568 ), 1569 gexec.NewPrefixedWriter( 1570 fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", ansiColorCode, name), 1571 ginkgo.GinkgoWriter, 1572 ), 1573 ) 1574 } 1575 1576 func (n *Network) GenerateCryptoConfig() { 1577 crypto, err := os.Create(n.CryptoConfigPath()) 1578 Expect(err).NotTo(HaveOccurred()) 1579 defer crypto.Close() 1580 1581 t, err := template.New("crypto").Parse(n.Templates.CryptoTemplate()) 1582 Expect(err).NotTo(HaveOccurred()) 1583 1584 pw := gexec.NewPrefixedWriter("[crypto-config.yaml] ", ginkgo.GinkgoWriter) 1585 err = t.Execute(io.MultiWriter(crypto, pw), n) 1586 Expect(err).NotTo(HaveOccurred()) 1587 } 1588 1589 func (n *Network) GenerateConfigTxConfig() { 1590 config, err := os.Create(n.ConfigTxConfigPath()) 1591 Expect(err).NotTo(HaveOccurred()) 1592 defer config.Close() 1593 1594 t, err := template.New("configtx").Parse(n.Templates.ConfigTxTemplate()) 1595 Expect(err).NotTo(HaveOccurred()) 1596 1597 pw := gexec.NewPrefixedWriter("[configtx.yaml] ", ginkgo.GinkgoWriter) 1598 err = t.Execute(io.MultiWriter(config, pw), n) 1599 Expect(err).NotTo(HaveOccurred()) 1600 } 1601 1602 func (n *Network) GenerateOrdererConfig(o *Orderer) { 1603 err := os.MkdirAll(n.OrdererDir(o), 0755) 1604 Expect(err).NotTo(HaveOccurred()) 1605 1606 orderer, err := os.Create(n.OrdererConfigPath(o)) 1607 Expect(err).NotTo(HaveOccurred()) 1608 defer orderer.Close() 1609 1610 t, err := template.New("orderer").Funcs(template.FuncMap{ 1611 "Orderer": func() *Orderer { return o }, 1612 "ToLower": func(s string) string { return strings.ToLower(s) }, 1613 "ReplaceAll": func(s, old, new string) string { return strings.Replace(s, old, new, -1) }, 1614 }).Parse(n.Templates.OrdererTemplate()) 1615 Expect(err).NotTo(HaveOccurred()) 1616 1617 pw := gexec.NewPrefixedWriter(fmt.Sprintf("[%s#orderer.yaml] ", o.ID()), ginkgo.GinkgoWriter) 1618 err = t.Execute(io.MultiWriter(orderer, pw), n) 1619 Expect(err).NotTo(HaveOccurred()) 1620 } 1621 1622 func (n *Network) GenerateCoreConfig(p *Peer) { 1623 err := os.MkdirAll(n.PeerDir(p), 0755) 1624 Expect(err).NotTo(HaveOccurred()) 1625 1626 core, err := os.Create(n.PeerConfigPath(p)) 1627 Expect(err).NotTo(HaveOccurred()) 1628 defer core.Close() 1629 1630 t, err := template.New("peer").Funcs(template.FuncMap{ 1631 "Peer": func() *Peer { return p }, 1632 "ToLower": func(s string) string { return strings.ToLower(s) }, 1633 "ReplaceAll": func(s, old, new string) string { return strings.Replace(s, old, new, -1) }, 1634 }).Parse(n.Templates.CoreTemplate()) 1635 Expect(err).NotTo(HaveOccurred()) 1636 1637 pw := gexec.NewPrefixedWriter(fmt.Sprintf("[%s#core.yaml] ", p.ID()), ginkgo.GinkgoWriter) 1638 err = t.Execute(io.MultiWriter(core, pw), n) 1639 Expect(err).NotTo(HaveOccurred()) 1640 }