github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/integration/nwo/network.go (about) 1 /* 2 Copyright hechain. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package nwo 8 9 import ( 10 "bytes" 11 "context" 12 "fmt" 13 "io" 14 "io/ioutil" 15 "net" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "runtime" 20 "sort" 21 "strconv" 22 "strings" 23 "sync" 24 "syscall" 25 "text/template" 26 "time" 27 28 docker "github.com/fsouza/go-dockerclient" 29 "github.com/hechain20/hechain/integration/nwo/commands" 30 "github.com/hechain20/hechain/integration/nwo/fabricconfig" 31 "github.com/hechain20/hechain/integration/nwo/runner" 32 "github.com/onsi/ginkgo" 33 . "github.com/onsi/gomega" 34 "github.com/onsi/gomega/gexec" 35 "github.com/onsi/gomega/gstruct" 36 "github.com/onsi/gomega/types" 37 "github.com/tedsuo/ifrit" 38 "github.com/tedsuo/ifrit/ginkgomon" 39 "github.com/tedsuo/ifrit/grouper" 40 "google.golang.org/grpc" 41 "google.golang.org/grpc/credentials" 42 "gopkg.in/yaml.v2" 43 ) 44 45 // Organization models information about an Organization. It includes 46 // the information needed to populate an MSP with cryptogen. 47 type Organization struct { 48 MSPID string `yaml:"msp_id,omitempty"` 49 MSPType string `yaml:"msp_type,omitempty"` 50 Name string `yaml:"name,omitempty"` 51 Domain string `yaml:"domain,omitempty"` 52 EnableNodeOUs bool `yaml:"enable_node_organizational_units"` 53 Users int `yaml:"users,omitempty"` 54 CA *CA `yaml:"ca,omitempty"` 55 } 56 57 type CA struct { 58 Hostname string `yaml:"hostname,omitempty"` 59 } 60 61 // A Consortium is a named collection of Organizations. It is used to populate 62 // the Orderer geneesis block profile. 63 type Consortium struct { 64 Name string `yaml:"name,omitempty"` 65 Organizations []string `yaml:"organizations,omitempty"` 66 } 67 68 // Consensus indicates the orderer types and how many broker and zookeeper 69 // instances. 70 type Consensus struct { 71 Type string `yaml:"type,omitempty"` 72 BootstrapMethod string `yaml:"bootstrap_method,omitempty"` 73 Brokers int `yaml:"brokers,omitempty"` 74 ZooKeepers int `yaml:"zookeepers,omitempty"` 75 ChannelParticipationEnabled bool `yaml:"channel_participation_enabled,omitempty"` 76 } 77 78 // The SystemChannel declares the name of the network system channel and its 79 // associated configtxgen profile name. 80 type SystemChannel struct { 81 Name string `yaml:"name,omitempty"` 82 Profile string `yaml:"profile,omitempty"` 83 } 84 85 // Channel associates a channel name with a configtxgen profile name. 86 type Channel struct { 87 Name string `yaml:"name,omitempty"` 88 Profile string `yaml:"profile,omitempty"` 89 BaseProfile string `yaml:"baseprofile,omitempty"` 90 } 91 92 // Orderer defines an orderer instance and its owning organization. 93 type Orderer struct { 94 Name string `yaml:"name,omitempty"` 95 Organization string `yaml:"organization,omitempty"` 96 } 97 98 // ID provides a unique identifier for an orderer instance. 99 func (o Orderer) ID() string { 100 return fmt.Sprintf("%s.%s", o.Organization, o.Name) 101 } 102 103 // Peer defines a peer instance, it's owning organization, and the list of 104 // channels that the peer should be joined to. 105 type Peer struct { 106 Name string `yaml:"name,omitempty"` 107 DevMode bool `yaml:"devmode,omitempty"` 108 Organization string `yaml:"organization,omitempty"` 109 Channels []*PeerChannel `yaml:"channels,omitempty"` 110 } 111 112 // PeerChannel names of the channel a peer should be joined to and whether or 113 // not the peer should be an anchor for the channel. 114 type PeerChannel struct { 115 Name string `yaml:"name,omitempty"` 116 Anchor bool `yaml:"anchor"` 117 } 118 119 // ID provides a unique identifier for a peer instance. 120 func (p *Peer) ID() string { 121 return fmt.Sprintf("%s.%s", p.Organization, p.Name) 122 } 123 124 // Anchor returns true if this peer is an anchor for any channel it has joined. 125 func (p *Peer) Anchor() bool { 126 for _, c := range p.Channels { 127 if c.Anchor { 128 return true 129 } 130 } 131 return false 132 } 133 134 // A profile encapsulates basic information for a configtxgen profile. 135 type Profile struct { 136 Name string `yaml:"name,omitempty"` 137 Orderers []string `yaml:"orderers,omitempty"` 138 Consortium string `yaml:"consortium,omitempty"` 139 Organizations []string `yaml:"organizations,omitempty"` 140 AppCapabilities []string `yaml:"app_capabilities,omitempty"` 141 ChannelCapabilities []string `yaml:"channel_capabilities,omitempty"` 142 } 143 144 // Network holds information about a fabric network. 145 type Network struct { 146 RootDir string 147 StartPort uint16 148 Components *Components 149 DockerClient *docker.Client 150 ExternalBuilders []fabricconfig.ExternalBuilder 151 NetworkID string 152 EventuallyTimeout time.Duration 153 SessionCreateInterval time.Duration 154 MetricsProvider string 155 StatsdEndpoint string 156 ClientAuthRequired bool 157 TLSEnabled bool 158 GatewayEnabled bool 159 160 PortsByBrokerID map[string]Ports 161 PortsByOrdererID map[string]Ports 162 PortsByPeerID map[string]Ports 163 Organizations []*Organization 164 SystemChannel *SystemChannel 165 Channels []*Channel 166 Consensus *Consensus 167 Orderers []*Orderer 168 Peers []*Peer 169 Profiles []*Profile 170 Consortiums []*Consortium 171 Templates *Templates 172 173 mutex sync.Locker 174 colorIndex uint 175 lastExecuted map[string]time.Time 176 } 177 178 // New creates a Network from a simple configuration. All generated or managed 179 // artifacts for the network will be located under rootDir. Ports will be 180 // allocated sequentially from the specified startPort. 181 func New(c *Config, rootDir string, dockerClient *docker.Client, startPort int, components *Components) *Network { 182 network := &Network{ 183 StartPort: uint16(startPort), 184 RootDir: rootDir, 185 Components: components, 186 DockerClient: dockerClient, 187 188 NetworkID: runner.UniqueName(), 189 EventuallyTimeout: time.Minute, 190 MetricsProvider: "prometheus", 191 PortsByBrokerID: map[string]Ports{}, 192 PortsByOrdererID: map[string]Ports{}, 193 PortsByPeerID: map[string]Ports{}, 194 195 Organizations: c.Organizations, 196 Consensus: c.Consensus, 197 Orderers: c.Orderers, 198 Peers: c.Peers, 199 SystemChannel: c.SystemChannel, 200 Channels: c.Channels, 201 Profiles: c.Profiles, 202 Consortiums: c.Consortiums, 203 Templates: c.Templates, 204 TLSEnabled: true, // Set TLS enabled as true for default 205 GatewayEnabled: true, // Set Gateway enabled as true for default 206 207 mutex: &sync.Mutex{}, 208 lastExecuted: make(map[string]time.Time), 209 } 210 211 // add the ccaas builder as well; that is built into the release directory 212 // so work that out based on current runtime. 213 // make integration-preqreqs have been updated to ensure this is built 214 cwd, err := os.Getwd() 215 Expect(err).NotTo(HaveOccurred()) 216 network.ExternalBuilders = []fabricconfig.ExternalBuilder{{ 217 Path: filepath.Join(cwd, "..", "externalbuilders", "binary"), 218 Name: "binary", 219 PropagateEnvironment: []string{"GOPROXY"}, 220 }, { 221 Path: filepath.Join(cwd, "..", "..", "release", fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH), "bin", "ccaas_builder"), 222 Name: "ccaas", 223 PropagateEnvironment: []string{"CHAINCODE_AS_A_SERVICE_BUILDER_CONFIG"}, 224 }} 225 226 if network.Templates == nil { 227 network.Templates = &Templates{} 228 } 229 if network.SessionCreateInterval == 0 { 230 network.SessionCreateInterval = time.Second 231 } 232 233 for i := 0; i < network.Consensus.Brokers; i++ { 234 ports := Ports{} 235 for _, portName := range BrokerPortNames() { 236 ports[portName] = network.ReservePort() 237 } 238 network.PortsByBrokerID[strconv.Itoa(i)] = ports 239 } 240 241 for _, o := range c.Orderers { 242 ports := Ports{} 243 for _, portName := range OrdererPortNames() { 244 ports[portName] = network.ReservePort() 245 } 246 network.PortsByOrdererID[o.ID()] = ports 247 } 248 249 for _, p := range c.Peers { 250 ports := Ports{} 251 for _, portName := range PeerPortNames() { 252 ports[portName] = network.ReservePort() 253 } 254 network.PortsByPeerID[p.ID()] = ports 255 } 256 257 if dockerClient != nil { 258 assertImagesExist(dockerClient, RequiredImages...) 259 } 260 261 return network 262 } 263 264 func assertImagesExist(dockerClient *docker.Client, images ...string) { 265 for _, imageName := range images { 266 images, err := dockerClient.ListImages(docker.ListImagesOptions{ 267 Filters: map[string][]string{"reference": {imageName}}, 268 }) 269 Expect(err).NotTo(HaveOccurred()) 270 271 if len(images) != 1 { 272 ginkgo.Fail(fmt.Sprintf("missing required image: %s", imageName), 1) 273 } 274 } 275 } 276 277 // AddOrg adds an organization to a network. 278 func (n *Network) AddOrg(o *Organization, peers ...*Peer) { 279 for _, p := range peers { 280 ports := Ports{} 281 for _, portName := range PeerPortNames() { 282 ports[portName] = n.ReservePort() 283 } 284 n.PortsByPeerID[p.ID()] = ports 285 n.Peers = append(n.Peers, p) 286 } 287 288 n.Organizations = append(n.Organizations, o) 289 n.Consortiums[0].Organizations = append(n.Consortiums[0].Organizations, o.Name) 290 } 291 292 // ConfigTxPath returns the path to the generated configtxgen configuration 293 // file. 294 func (n *Network) ConfigTxConfigPath() string { 295 return filepath.Join(n.RootDir, "configtx.yaml") 296 } 297 298 // CryptoPath returns the path to the directory where cryptogen will place its 299 // generated artifacts. 300 func (n *Network) CryptoPath() string { 301 return filepath.Join(n.RootDir, "crypto") 302 } 303 304 // CryptoConfigPath returns the path to the generated cryptogen configuration 305 // file. 306 func (n *Network) CryptoConfigPath() string { 307 return filepath.Join(n.RootDir, "crypto-config.yaml") 308 } 309 310 // OutputBlockPath returns the path to the genesis block for the named system 311 // channel. 312 func (n *Network) OutputBlockPath(channelName string) string { 313 return filepath.Join(n.RootDir, fmt.Sprintf("%s_block.pb", channelName)) 314 } 315 316 // CreateChannelTxPath returns the path to the create channel transaction for 317 // the named channel. 318 func (n *Network) CreateChannelTxPath(channelName string) string { 319 return filepath.Join(n.RootDir, fmt.Sprintf("%s_tx.pb", channelName)) 320 } 321 322 // OrdererDir returns the path to the configuration directory for the specified 323 // Orderer. 324 func (n *Network) OrdererDir(o *Orderer) string { 325 return filepath.Join(n.RootDir, "orderers", o.ID()) 326 } 327 328 // OrdererConfigPath returns the path to the orderer configuration document for 329 // the specified Orderer. 330 func (n *Network) OrdererConfigPath(o *Orderer) string { 331 return filepath.Join(n.OrdererDir(o), "orderer.yaml") 332 } 333 334 // ReadOrdererConfig unmarshals an orderer's orderer.yaml and returns an 335 // object approximating its contents. 336 func (n *Network) ReadOrdererConfig(o *Orderer) *fabricconfig.Orderer { 337 var orderer fabricconfig.Orderer 338 ordererBytes, err := ioutil.ReadFile(n.OrdererConfigPath(o)) 339 Expect(err).NotTo(HaveOccurred()) 340 341 err = yaml.Unmarshal(ordererBytes, &orderer) 342 Expect(err).NotTo(HaveOccurred()) 343 344 return &orderer 345 } 346 347 // WriteOrdererConfig serializes the provided configuration as the specified 348 // orderer's orderer.yaml document. 349 func (n *Network) WriteOrdererConfig(o *Orderer, config *fabricconfig.Orderer) { 350 ordererBytes, err := yaml.Marshal(config) 351 Expect(err).NotTo(HaveOccurred()) 352 353 err = ioutil.WriteFile(n.OrdererConfigPath(o), ordererBytes, 0o644) 354 Expect(err).NotTo(HaveOccurred()) 355 356 pw := gexec.NewPrefixedWriter(fmt.Sprintf("[updated-%s#orderer.yaml] ", o.ID()), ginkgo.GinkgoWriter) 357 _, err = pw.Write(ordererBytes) 358 Expect(err).NotTo(HaveOccurred()) 359 } 360 361 // ReadConfigTxConfig unmarshals the configtx.yaml and returns an 362 // object approximating its contents. 363 func (n *Network) ReadConfigTxConfig() *fabricconfig.ConfigTx { 364 var configtx fabricconfig.ConfigTx 365 configtxBytes, err := ioutil.ReadFile(n.ConfigTxConfigPath()) 366 Expect(err).NotTo(HaveOccurred()) 367 368 err = yaml.Unmarshal(configtxBytes, &configtx) 369 Expect(err).NotTo(HaveOccurred()) 370 371 return &configtx 372 } 373 374 // WriteConfigTxConfig serializes the provided configuration to configtx.yaml. 375 func (n *Network) WriteConfigTxConfig(config *fabricconfig.ConfigTx) { 376 configtxBytes, err := yaml.Marshal(config) 377 Expect(err).NotTo(HaveOccurred()) 378 379 err = ioutil.WriteFile(n.ConfigTxConfigPath(), configtxBytes, 0o644) 380 Expect(err).NotTo(HaveOccurred()) 381 } 382 383 // PeerDir returns the path to the configuration directory for the specified 384 // Peer. 385 func (n *Network) PeerDir(p *Peer) string { 386 return filepath.Join(n.RootDir, "peers", p.ID()) 387 } 388 389 // PeerConfigPath returns the path to the peer configuration document for the 390 // specified peer. 391 func (n *Network) PeerConfigPath(p *Peer) string { 392 return filepath.Join(n.PeerDir(p), "core.yaml") 393 } 394 395 // PeerLedgerDir returns the ledger root directory for the specified peer. 396 func (n *Network) PeerLedgerDir(p *Peer) string { 397 return filepath.Join(n.PeerDir(p), "filesystem/ledgersData") 398 } 399 400 // ReadPeerConfig unmarshals a peer's core.yaml and returns an object 401 // approximating its contents. 402 func (n *Network) ReadPeerConfig(p *Peer) *fabricconfig.Core { 403 var core fabricconfig.Core 404 coreBytes, err := ioutil.ReadFile(n.PeerConfigPath(p)) 405 Expect(err).NotTo(HaveOccurred()) 406 407 err = yaml.Unmarshal(coreBytes, &core) 408 Expect(err).NotTo(HaveOccurred()) 409 410 return &core 411 } 412 413 // WritePeerConfig serializes the provided configuration as the specified 414 // peer's core.yaml document. 415 func (n *Network) WritePeerConfig(p *Peer, config *fabricconfig.Core) { 416 coreBytes, err := yaml.Marshal(config) 417 Expect(err).NotTo(HaveOccurred()) 418 419 err = ioutil.WriteFile(n.PeerConfigPath(p), coreBytes, 0o644) 420 Expect(err).NotTo(HaveOccurred()) 421 422 pw := gexec.NewPrefixedWriter(fmt.Sprintf("[updated-%s#core.yaml] ", p.ID()), ginkgo.GinkgoWriter) 423 _, err = pw.Write(coreBytes) 424 Expect(err).NotTo(HaveOccurred()) 425 } 426 427 // peerUserCryptoDir returns the path to the directory containing the 428 // certificates and keys for the specified user of the peer. 429 func (n *Network) peerUserCryptoDir(p *Peer, user, cryptoMaterialType string) string { 430 org := n.Organization(p.Organization) 431 Expect(org).NotTo(BeNil()) 432 433 return n.userCryptoDir(org, "peerOrganizations", user, cryptoMaterialType) 434 } 435 436 // ordererUserCryptoDir returns the path to the directory containing the 437 // certificates and keys for the specified user of the orderer. 438 func (n *Network) ordererUserCryptoDir(o *Orderer, user, cryptoMaterialType string) string { 439 org := n.Organization(o.Organization) 440 Expect(org).NotTo(BeNil()) 441 442 return n.userCryptoDir(org, "ordererOrganizations", user, cryptoMaterialType) 443 } 444 445 // userCryptoDir returns the path to the folder with crypto materials for either peers or orderer organizations 446 // specific user 447 func (n *Network) userCryptoDir(org *Organization, nodeOrganizationType, user, cryptoMaterialType string) string { 448 return filepath.Join( 449 n.RootDir, 450 "crypto", 451 nodeOrganizationType, 452 org.Domain, 453 "users", 454 fmt.Sprintf("%s@%s", user, org.Domain), 455 cryptoMaterialType, 456 ) 457 } 458 459 // PeerOrgCADir returns the path to the folder containing the CA certificate(s) and/or 460 // keys for the specified peer organization. 461 func (n *Network) PeerOrgCADir(o *Organization) string { 462 return filepath.Join( 463 n.CryptoPath(), 464 "peerOrganizations", 465 o.Domain, 466 "ca", 467 ) 468 } 469 470 // OrdererOrgCADir returns the path to the folder containing the CA certificate(s) and/or 471 // keys for the specified orderer organization. 472 func (n *Network) OrdererOrgCADir(o *Organization) string { 473 return filepath.Join( 474 n.CryptoPath(), 475 "ordererOrganizations", 476 o.Domain, 477 "ca", 478 ) 479 } 480 481 // PeerUserMSPDir returns the path to the MSP directory containing the 482 // certificates and keys for the specified user of the peer. 483 func (n *Network) PeerUserMSPDir(p *Peer, user string) string { 484 return n.peerUserCryptoDir(p, user, "msp") 485 } 486 487 // IdemixUserMSPDir returns the path to the MSP directory containing the 488 // idemix-related crypto material for the specified user of the organization. 489 func (n *Network) IdemixUserMSPDir(o *Organization, user string) string { 490 return n.userCryptoDir(o, "peerOrganizations", user, "") 491 } 492 493 // OrdererUserMSPDir returns the path to the MSP directory containing the 494 // certificates and keys for the specified user of the peer. 495 func (n *Network) OrdererUserMSPDir(o *Orderer, user string) string { 496 return n.ordererUserCryptoDir(o, user, "msp") 497 } 498 499 // PeerUserTLSDir returns the path to the TLS directory containing the 500 // certificates and keys for the specified user of the peer. 501 func (n *Network) PeerUserTLSDir(p *Peer, user string) string { 502 return n.peerUserCryptoDir(p, user, "tls") 503 } 504 505 // PeerUserCert returns the path to the certificate for the specified user in 506 // the peer organization. 507 func (n *Network) PeerUserCert(p *Peer, user string) string { 508 org := n.Organization(p.Organization) 509 Expect(org).NotTo(BeNil()) 510 511 return filepath.Join( 512 n.PeerUserMSPDir(p, user), 513 "signcerts", 514 fmt.Sprintf("%s@%s-cert.pem", user, org.Domain), 515 ) 516 } 517 518 // PeerCACert returns the path to the CA certificate for the peer 519 // organization. 520 func (n *Network) PeerCACert(p *Peer) string { 521 org := n.Organization(p.Organization) 522 Expect(org).NotTo(BeNil()) 523 524 return filepath.Join( 525 n.PeerOrgMSPDir(org), 526 "cacerts", 527 fmt.Sprintf("ca.%s-cert.pem", org.Domain), 528 ) 529 } 530 531 // OrdererUserCert returns the path to the certificate for the specified user in 532 // the orderer organization. 533 func (n *Network) OrdererUserCert(o *Orderer, user string) string { 534 org := n.Organization(o.Organization) 535 Expect(org).NotTo(BeNil()) 536 537 return filepath.Join( 538 n.OrdererUserMSPDir(o, user), 539 "signcerts", 540 fmt.Sprintf("%s@%s-cert.pem", user, org.Domain), 541 ) 542 } 543 544 // OrdererCACert returns the path to the CA certificate for the orderer 545 // organization. 546 func (n *Network) OrdererCACert(o *Orderer) string { 547 org := n.Organization(o.Organization) 548 Expect(org).NotTo(BeNil()) 549 550 return filepath.Join( 551 n.OrdererOrgMSPDir(org), 552 "cacerts", 553 fmt.Sprintf("ca.%s-cert.pem", org.Domain), 554 ) 555 } 556 557 // PeerUserKey returns the path to the private key for the specified user in 558 // the peer organization. 559 func (n *Network) PeerUserKey(p *Peer, user string) string { 560 org := n.Organization(p.Organization) 561 Expect(org).NotTo(BeNil()) 562 563 keystore := filepath.Join( 564 n.PeerUserMSPDir(p, user), 565 "keystore", 566 ) 567 568 // file names are the SKI and non-deterministic 569 keys, err := ioutil.ReadDir(keystore) 570 Expect(err).NotTo(HaveOccurred()) 571 Expect(keys).To(HaveLen(1)) 572 573 return filepath.Join(keystore, keys[0].Name()) 574 } 575 576 // OrdererUserKey returns the path to the private key for the specified user in 577 // the orderer organization. 578 func (n *Network) OrdererUserKey(o *Orderer, user string) string { 579 org := n.Organization(o.Organization) 580 Expect(org).NotTo(BeNil()) 581 582 keystore := filepath.Join( 583 n.OrdererUserMSPDir(o, user), 584 "keystore", 585 ) 586 587 // file names are the SKI and non-deterministic 588 keys, err := ioutil.ReadDir(keystore) 589 Expect(err).NotTo(HaveOccurred()) 590 Expect(keys).To(HaveLen(1)) 591 592 return filepath.Join(keystore, keys[0].Name()) 593 } 594 595 // PeerUserSigner returns a SigningIdentity representing the specified user in 596 // the peer organization. 597 func (n *Network) PeerUserSigner(p *Peer, user string) *SigningIdentity { 598 return &SigningIdentity{ 599 CertPath: n.PeerUserCert(p, user), 600 KeyPath: n.PeerUserKey(p, user), 601 MSPID: n.Organization(p.Organization).MSPID, 602 } 603 } 604 605 // OrdererUserSigner returns a SigningIdentity representing the specified user in 606 // the orderer organization. 607 func (n *Network) OrdererUserSigner(o *Orderer, user string) *SigningIdentity { 608 return &SigningIdentity{ 609 CertPath: n.OrdererUserCert(o, user), 610 KeyPath: n.OrdererUserKey(o, user), 611 MSPID: n.Organization(o.Organization).MSPID, 612 } 613 } 614 615 // peerLocalCryptoDir returns the path to the local crypto directory for the peer. 616 func (n *Network) peerLocalCryptoDir(p *Peer, cryptoType string) string { 617 org := n.Organization(p.Organization) 618 Expect(org).NotTo(BeNil()) 619 620 return filepath.Join( 621 n.RootDir, 622 "crypto", 623 "peerOrganizations", 624 org.Domain, 625 "peers", 626 fmt.Sprintf("%s.%s", p.Name, org.Domain), 627 cryptoType, 628 ) 629 } 630 631 // PeerLocalMSPDir returns the path to the local MSP directory for the peer. 632 func (n *Network) PeerLocalMSPDir(p *Peer) string { 633 return n.peerLocalCryptoDir(p, "msp") 634 } 635 636 // PeerLocalTLSDir returns the path to the local TLS directory for the peer. 637 func (n *Network) PeerLocalTLSDir(p *Peer) string { 638 return n.peerLocalCryptoDir(p, "tls") 639 } 640 641 // PeerCert returns the path to the peer's certificate. 642 func (n *Network) PeerCert(p *Peer) string { 643 org := n.Organization(p.Organization) 644 Expect(org).NotTo(BeNil()) 645 646 return filepath.Join( 647 n.PeerLocalMSPDir(p), 648 "signcerts", 649 fmt.Sprintf("%s.%s-cert.pem", p.Name, org.Domain), 650 ) 651 } 652 653 // PeerOrgMSPDir returns the path to the MSP directory of the Peer organization. 654 func (n *Network) PeerOrgMSPDir(org *Organization) string { 655 return filepath.Join( 656 n.RootDir, 657 "crypto", 658 "peerOrganizations", 659 org.Domain, 660 "msp", 661 ) 662 } 663 664 func (n *Network) IdemixOrgMSPDir(org *Organization) string { 665 return filepath.Join( 666 n.RootDir, 667 "crypto", 668 "peerOrganizations", 669 org.Domain, 670 ) 671 } 672 673 // OrdererOrgMSPDir returns the path to the MSP directory of the Orderer 674 // organization. 675 func (n *Network) OrdererOrgMSPDir(o *Organization) string { 676 return filepath.Join( 677 n.RootDir, 678 "crypto", 679 "ordererOrganizations", 680 o.Domain, 681 "msp", 682 ) 683 } 684 685 // OrdererLocalCryptoDir returns the path to the local crypto directory for the 686 // Orderer. 687 func (n *Network) OrdererLocalCryptoDir(o *Orderer, cryptoType string) string { 688 org := n.Organization(o.Organization) 689 Expect(org).NotTo(BeNil()) 690 691 return filepath.Join( 692 n.RootDir, 693 "crypto", 694 "ordererOrganizations", 695 org.Domain, 696 "orderers", 697 fmt.Sprintf("%s.%s", o.Name, org.Domain), 698 cryptoType, 699 ) 700 } 701 702 // OrdererLocalMSPDir returns the path to the local MSP directory for the 703 // Orderer. 704 func (n *Network) OrdererLocalMSPDir(o *Orderer) string { 705 return n.OrdererLocalCryptoDir(o, "msp") 706 } 707 708 // OrdererLocalTLSDir returns the path to the local TLS directory for the 709 // Orderer. 710 func (n *Network) OrdererLocalTLSDir(o *Orderer) string { 711 return n.OrdererLocalCryptoDir(o, "tls") 712 } 713 714 // ProfileForChannel gets the configtxgen profile name associated with the 715 // specified channel. 716 func (n *Network) ProfileForChannel(channelName string) string { 717 for _, ch := range n.Channels { 718 if ch.Name == channelName { 719 return ch.Profile 720 } 721 } 722 return "" 723 } 724 725 // CACertsBundlePath returns the path to the bundle of CA certificates for the 726 // network. This bundle is used when connecting to peers. 727 func (n *Network) CACertsBundlePath() string { 728 return filepath.Join( 729 n.RootDir, 730 "crypto", 731 "ca-certs.pem", 732 ) 733 } 734 735 // GenerateConfigTree generates the configuration documents required to 736 // bootstrap a fabric network. A configuration file will be generated for 737 // cryptogen, configtxgen, and for each peer and orderer. The contents of the 738 // documents will be based on the Config used to create the Network. 739 // 740 // When this method completes, the resulting tree will look something like 741 // this: 742 // 743 // ${rootDir}/configtx.yaml 744 // ${rootDir}/crypto-config.yaml 745 // ${rootDir}/orderers/orderer0.orderer-org/orderer.yaml 746 // ${rootDir}/peers/peer0.org1/core.yaml 747 // ${rootDir}/peers/peer0.org2/core.yaml 748 // ${rootDir}/peers/peer1.org1/core.yaml 749 // ${rootDir}/peers/peer1.org2/core.yaml 750 // 751 func (n *Network) GenerateConfigTree() { 752 n.GenerateCryptoConfig() 753 n.GenerateConfigTxConfig() 754 for _, o := range n.Orderers { 755 n.GenerateOrdererConfig(o) 756 } 757 for _, p := range n.Peers { 758 n.GenerateCoreConfig(p) 759 } 760 } 761 762 // Bootstrap generates the cryptographic material, orderer system channel 763 // genesis block, and create channel transactions needed to run a fabric 764 // network. 765 // 766 // The cryptogen tool is used to create crypto material from the contents of 767 // ${rootDir}/crypto-config.yaml. The generated artifacts will be placed in 768 // ${rootDir}/crypto/... 769 // 770 // The gensis block is generated from the profile referenced by the 771 // SystemChannel.Profile attribute. The block is written to 772 // ${rootDir}/${SystemChannel.Name}_block.pb. 773 // 774 // The create channel transactions are generated for each Channel referenced by 775 // the Network using the channel's Profile attribute. The transactions are 776 // written to ${rootDir}/${Channel.Name}_tx.pb. 777 func (n *Network) Bootstrap() { 778 if n.DockerClient != nil { 779 n.CreateDockerNetwork() 780 } 781 782 sess, err := n.Cryptogen(commands.Generate{ 783 Config: n.CryptoConfigPath(), 784 Output: n.CryptoPath(), 785 }) 786 Expect(err).NotTo(HaveOccurred()) 787 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 788 789 n.bootstrapIdemix() 790 791 sess, err = n.ConfigTxGen(commands.OutputBlock{ 792 ChannelID: n.SystemChannel.Name, 793 Profile: n.SystemChannel.Profile, 794 ConfigPath: n.RootDir, 795 OutputBlock: n.OutputBlockPath(n.SystemChannel.Name), 796 }) 797 Expect(err).NotTo(HaveOccurred()) 798 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 799 800 for _, c := range n.Channels { 801 sess, err := n.ConfigTxGen(commands.CreateChannelTx{ 802 ChannelID: c.Name, 803 Profile: c.Profile, 804 BaseProfile: c.BaseProfile, 805 ConfigPath: n.RootDir, 806 OutputCreateChannelTx: n.CreateChannelTxPath(c.Name), 807 }) 808 Expect(err).NotTo(HaveOccurred()) 809 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 810 } 811 812 n.ConcatenateTLSCACertificates() 813 } 814 815 func (n *Network) CreateDockerNetwork() { 816 _, err := n.DockerClient.CreateNetwork( 817 docker.CreateNetworkOptions{ 818 Name: n.NetworkID, 819 Driver: "bridge", 820 }, 821 ) 822 Expect(err).NotTo(HaveOccurred()) 823 824 if runtime.GOOS == "darwin" { 825 n.checkDockerNetworks() 826 } 827 } 828 829 // checkDockerNetworks attempts to discover if the docker network configuration 830 // will prevent a container from accessing the host. This commonly happens when 831 // using Docker for Mac on home networks because most home routers provide DHCP 832 // addresses from 192.168.1.0/24 and the default Docker daemon config uses 833 // 192.168.0.0/20 as one of the default local address pools. 834 // 835 // https://github.com/moby/libnetwork/blob/1a17fb36132631a95fe6bb055b91e24a516ad81d/ipamutils/utils.go#L18-L20 836 // 837 // Docker can be configured to use different addresses by adding an 838 // appropriate default-address-pools configuration element to "daemon.json". 839 // 840 // For example: 841 // "default-address-pools":[ 842 // {"base":"172.30.0.0/16","size":24}, 843 // {"base":"172.31.0.0/16","size":24} 844 // ] 845 func (n *Network) checkDockerNetworks() { 846 hostAddrs := hostIPv4Addrs() 847 for _, nw := range n.dockerIPNets() { 848 for _, a := range hostAddrs { 849 if nw.Contains(a) { 850 fmt.Fprintf(ginkgo.GinkgoWriter, "\x1b[01;37;41mWARNING: docker network %s overlaps with host address %s.\x1b[0m\n", nw, a) 851 fmt.Fprintf(ginkgo.GinkgoWriter, "\x1b[01;37;41mDocker containers may not have connectivity causing chaincode registration to fail with 'no route to host'.\x1b[0m\n") 852 } 853 } 854 } 855 } 856 857 func (n *Network) dockerIPNets() []*net.IPNet { 858 dockerNetworks, err := n.DockerClient.ListNetworks() 859 Expect(err).NotTo(HaveOccurred()) 860 861 var nets []*net.IPNet 862 for _, nw := range dockerNetworks { 863 for _, ipconf := range nw.IPAM.Config { 864 if ipconf.Subnet != "" { 865 _, ipn, err := net.ParseCIDR(ipconf.Subnet) 866 Expect(err).NotTo(HaveOccurred()) 867 nets = append(nets, ipn) 868 } 869 } 870 } 871 return nets 872 } 873 874 func hostIPv4Addrs() []net.IP { 875 interfaces, err := net.Interfaces() 876 Expect(err).NotTo(HaveOccurred()) 877 878 var addresses []net.IP 879 for _, i := range interfaces { 880 addrs, err := i.Addrs() 881 Expect(err).NotTo(HaveOccurred()) 882 883 for _, a := range addrs { 884 a := a 885 switch v := a.(type) { 886 case *net.IPAddr: 887 if v.IP.To4() != nil { 888 addresses = append(addresses, v.IP) 889 } 890 case *net.IPNet: 891 if v.IP.To4() != nil { 892 addresses = append(addresses, v.IP) 893 } 894 } 895 } 896 } 897 return addresses 898 } 899 900 // bootstrapIdemix creates the idemix-related crypto material 901 func (n *Network) bootstrapIdemix() { 902 for j, org := range n.IdemixOrgs() { 903 output := n.IdemixOrgMSPDir(org) 904 // - ca-keygen 905 sess, err := n.Idemixgen(commands.CAKeyGen{ 906 Output: output, 907 }) 908 Expect(err).NotTo(HaveOccurred()) 909 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 910 911 // - signerconfig 912 usersOutput := filepath.Join(n.IdemixOrgMSPDir(org), "users") 913 userOutput := filepath.Join(usersOutput, "User1@"+org.Domain) 914 sess, err = n.Idemixgen(commands.SignerConfig{ 915 CAInput: output, 916 Output: userOutput, 917 OrgUnit: org.Domain, 918 EnrollmentID: "User1", 919 RevocationHandle: fmt.Sprintf("11%d", j), 920 }) 921 Expect(err).NotTo(HaveOccurred()) 922 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 923 } 924 } 925 926 // ConcatenateTLSCACertificates concatenates all TLS CA certificates into a 927 // single file to be used by peer CLI. 928 func (n *Network) ConcatenateTLSCACertificates() { 929 bundle := &bytes.Buffer{} 930 for _, tlsCertPath := range n.listTLSCACertificates() { 931 certBytes, err := ioutil.ReadFile(tlsCertPath) 932 Expect(err).NotTo(HaveOccurred()) 933 bundle.Write(certBytes) 934 } 935 err := ioutil.WriteFile(n.CACertsBundlePath(), bundle.Bytes(), 0o660) 936 Expect(err).NotTo(HaveOccurred()) 937 } 938 939 // listTLSCACertificates returns the paths of all TLS CA certificates in the 940 // network, across all organizations. 941 func (n *Network) listTLSCACertificates() []string { 942 fileName2Path := make(map[string]string) 943 filepath.Walk(filepath.Join(n.RootDir, "crypto"), func(path string, info os.FileInfo, err error) error { 944 // File starts with "tlsca" and has "-cert.pem" in it 945 if strings.HasPrefix(info.Name(), "tlsca") && strings.Contains(info.Name(), "-cert.pem") { 946 fileName2Path[info.Name()] = path 947 } 948 return nil 949 }) 950 951 var tlsCACertificates []string 952 for _, path := range fileName2Path { 953 tlsCACertificates = append(tlsCACertificates, path) 954 } 955 return tlsCACertificates 956 } 957 958 // Cleanup attempts to cleanup docker related artifacts that may 959 // have been created by the network. 960 func (n *Network) Cleanup() { 961 if n == nil || n.DockerClient == nil { 962 return 963 } 964 965 nw, err := n.DockerClient.NetworkInfo(n.NetworkID) 966 Expect(err).NotTo(HaveOccurred()) 967 968 err = n.DockerClient.RemoveNetwork(nw.ID) 969 Expect(err).NotTo(HaveOccurred()) 970 971 containers, err := n.DockerClient.ListContainers(docker.ListContainersOptions{All: true}) 972 Expect(err).NotTo(HaveOccurred()) 973 for _, c := range containers { 974 for _, name := range c.Names { 975 if strings.HasPrefix(name, "/"+n.NetworkID) { 976 err := n.DockerClient.RemoveContainer(docker.RemoveContainerOptions{ID: c.ID, Force: true}) 977 Expect(err).NotTo(HaveOccurred()) 978 break 979 } 980 } 981 } 982 983 images, err := n.DockerClient.ListImages(docker.ListImagesOptions{All: true}) 984 Expect(err).NotTo(HaveOccurred()) 985 for _, i := range images { 986 for _, tag := range i.RepoTags { 987 if strings.HasPrefix(tag, n.NetworkID) { 988 err := n.DockerClient.RemoveImage(i.ID) 989 Expect(err).NotTo(HaveOccurred()) 990 break 991 } 992 } 993 } 994 } 995 996 // CreateAndJoinChannels will create all channels specified in the config that 997 // are referenced by peers. The referencing peers will then be joined to the 998 // channel(s). 999 // 1000 // The network must be running before this is called. 1001 func (n *Network) CreateAndJoinChannels(o *Orderer) { 1002 for _, c := range n.Channels { 1003 n.CreateAndJoinChannel(o, c.Name) 1004 } 1005 } 1006 1007 // CreateAndJoinChannel will create the specified channel. The referencing 1008 // peers will then be joined to the channel. 1009 // 1010 // The network must be running before this is called. 1011 func (n *Network) CreateAndJoinChannel(o *Orderer, channelName string) { 1012 peers := n.PeersWithChannel(channelName) 1013 if len(peers) == 0 { 1014 return 1015 } 1016 1017 n.CreateChannel(channelName, o, peers[0]) 1018 n.JoinChannel(channelName, o, peers...) 1019 } 1020 1021 // UpdateChannelAnchors determines the anchor peers for the specified channel, 1022 // creates an anchor peer update transaction for each organization, and submits 1023 // the update transactions to the orderer. 1024 func (n *Network) UpdateChannelAnchors(o *Orderer, channelName string) { 1025 tempFile, err := ioutil.TempFile("", "update-anchors") 1026 Expect(err).NotTo(HaveOccurred()) 1027 tempFile.Close() 1028 defer os.Remove(tempFile.Name()) 1029 1030 peersByOrg := map[string]*Peer{} 1031 for _, p := range n.AnchorsForChannel(channelName) { 1032 peersByOrg[p.Organization] = p 1033 } 1034 1035 for orgName, p := range peersByOrg { 1036 anchorUpdate := commands.OutputAnchorPeersUpdate{ 1037 OutputAnchorPeersUpdate: tempFile.Name(), 1038 ChannelID: channelName, 1039 Profile: n.ProfileForChannel(channelName), 1040 ConfigPath: n.RootDir, 1041 AsOrg: orgName, 1042 } 1043 sess, err := n.ConfigTxGen(anchorUpdate) 1044 Expect(err).NotTo(HaveOccurred()) 1045 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1046 1047 sess, err = n.PeerAdminSession(p, commands.ChannelUpdate{ 1048 ChannelID: channelName, 1049 Orderer: n.OrdererAddress(o, ListenPort), 1050 File: tempFile.Name(), 1051 ClientAuth: n.ClientAuthRequired, 1052 }) 1053 Expect(err).NotTo(HaveOccurred()) 1054 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1055 } 1056 } 1057 1058 // VerifyMembership checks that each peer has discovered the expected peers in 1059 // the network. 1060 func (n *Network) VerifyMembership(expectedPeers []*Peer, channel string, chaincodes ...string) { 1061 // all peers currently include _lifecycle as an available chaincode 1062 chaincodes = append(chaincodes, "_lifecycle") 1063 expectedDiscoveredPeerMatchers := make([]types.GomegaMatcher, len(expectedPeers)) 1064 for i, peer := range expectedPeers { 1065 expectedDiscoveredPeerMatchers[i] = n.discoveredPeerMatcher(peer, chaincodes...) 1066 } 1067 for _, peer := range expectedPeers { 1068 Eventually(DiscoverPeers(n, peer, "User1", channel), n.EventuallyTimeout).Should(ConsistOf(expectedDiscoveredPeerMatchers)) 1069 } 1070 } 1071 1072 func (n *Network) discoveredPeerMatcher(p *Peer, chaincodes ...string) types.GomegaMatcher { 1073 peerCert, err := ioutil.ReadFile(n.PeerCert(p)) 1074 Expect(err).NotTo(HaveOccurred()) 1075 1076 var ccs []interface{} 1077 for _, cc := range chaincodes { 1078 ccs = append(ccs, cc) 1079 } 1080 return gstruct.MatchAllFields(gstruct.Fields{ 1081 "MSPID": Equal(n.Organization(p.Organization).MSPID), 1082 "Endpoint": Equal(fmt.Sprintf("127.0.0.1:%d", n.PeerPort(p, ListenPort))), 1083 "Identity": Equal(string(peerCert)), 1084 "Chaincodes": ContainElements(ccs...), 1085 }) 1086 } 1087 1088 // CreateChannel will submit an existing create channel transaction to the 1089 // specified orderer. The channel transaction must exist at the location 1090 // returned by CreateChannelTxPath. Optionally, additional signers may be 1091 // included in the case where the channel creation tx modifies other aspects of 1092 // the channel config for the new channel. 1093 // 1094 // The orderer must be running when this is called. 1095 func (n *Network) CreateChannel(channelName string, o *Orderer, p *Peer, additionalSigners ...interface{}) { 1096 channelCreateTxPath := n.CreateChannelTxPath(channelName) 1097 n.signConfigTransaction(channelCreateTxPath, p, additionalSigners...) 1098 1099 createChannel := func() int { 1100 sess, err := n.PeerAdminSession(p, commands.ChannelCreate{ 1101 ChannelID: channelName, 1102 Orderer: n.OrdererAddress(o, ListenPort), 1103 File: channelCreateTxPath, 1104 OutputBlock: "/dev/null", 1105 ClientAuth: n.ClientAuthRequired, 1106 }) 1107 Expect(err).NotTo(HaveOccurred()) 1108 return sess.Wait(n.EventuallyTimeout).ExitCode() 1109 } 1110 Eventually(createChannel, n.EventuallyTimeout).Should(Equal(0)) 1111 } 1112 1113 // CreateChannelExitCode will submit an existing create channel transaction to 1114 // the specified orderer, wait for the operation to complete, and return the 1115 // exit status of the "peer channel create" command. 1116 // 1117 // The channel transaction must exist at the location returned by 1118 // CreateChannelTxPath and the orderer must be running when this is called. 1119 func (n *Network) CreateChannelExitCode(channelName string, o *Orderer, p *Peer, additionalSigners ...interface{}) int { 1120 channelCreateTxPath := n.CreateChannelTxPath(channelName) 1121 n.signConfigTransaction(channelCreateTxPath, p, additionalSigners...) 1122 1123 sess, err := n.PeerAdminSession(p, commands.ChannelCreate{ 1124 ChannelID: channelName, 1125 Orderer: n.OrdererAddress(o, ListenPort), 1126 File: channelCreateTxPath, 1127 OutputBlock: "/dev/null", 1128 ClientAuth: n.ClientAuthRequired, 1129 }) 1130 Expect(err).NotTo(HaveOccurred()) 1131 return sess.Wait(n.EventuallyTimeout).ExitCode() 1132 } 1133 1134 func (n *Network) signConfigTransaction(channelTxPath string, submittingPeer *Peer, signers ...interface{}) { 1135 for _, signer := range signers { 1136 switch signer := signer.(type) { 1137 case *Peer: 1138 sess, err := n.PeerAdminSession(signer, commands.SignConfigTx{ 1139 File: channelTxPath, 1140 ClientAuth: n.ClientAuthRequired, 1141 }) 1142 Expect(err).NotTo(HaveOccurred()) 1143 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1144 1145 case *Orderer: 1146 sess, err := n.OrdererAdminSession(signer, submittingPeer, commands.SignConfigTx{ 1147 File: channelTxPath, 1148 ClientAuth: n.ClientAuthRequired, 1149 }) 1150 Expect(err).NotTo(HaveOccurred()) 1151 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1152 1153 default: 1154 panic(fmt.Sprintf("unknown signer type %T, expect Peer or Orderer", signer)) 1155 } 1156 } 1157 } 1158 1159 // JoinChannel will join peers to the specified channel. The orderer is used to 1160 // obtain the current configuration block for the channel. 1161 // 1162 // The orderer and listed peers must be running before this is called. 1163 func (n *Network) JoinChannel(name string, o *Orderer, peers ...*Peer) { 1164 if len(peers) == 0 { 1165 return 1166 } 1167 1168 tempFile, err := ioutil.TempFile("", "genesis-block") 1169 Expect(err).NotTo(HaveOccurred()) 1170 tempFile.Close() 1171 defer os.Remove(tempFile.Name()) 1172 1173 sess, err := n.PeerAdminSession(peers[0], commands.ChannelFetch{ 1174 Block: "0", 1175 ChannelID: name, 1176 Orderer: n.OrdererAddress(o, ListenPort), 1177 OutputFile: tempFile.Name(), 1178 ClientAuth: n.ClientAuthRequired, 1179 }) 1180 Expect(err).NotTo(HaveOccurred()) 1181 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1182 1183 for _, p := range peers { 1184 sess, err := n.PeerAdminSession(p, commands.ChannelJoin{ 1185 BlockPath: tempFile.Name(), 1186 ClientAuth: n.ClientAuthRequired, 1187 }) 1188 Expect(err).NotTo(HaveOccurred()) 1189 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1190 } 1191 } 1192 1193 func (n *Network) JoinChannelBySnapshot(snapshotDir string, peers ...*Peer) { 1194 if len(peers) == 0 { 1195 return 1196 } 1197 1198 for _, p := range peers { 1199 sess, err := n.PeerAdminSession(p, commands.ChannelJoinBySnapshot{ 1200 SnapshotPath: snapshotDir, 1201 ClientAuth: n.ClientAuthRequired, 1202 }) 1203 Expect(err).NotTo(HaveOccurred()) 1204 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1205 } 1206 } 1207 1208 func (n *Network) JoinBySnapshotStatus(p *Peer) string { 1209 sess, err := n.PeerAdminSession(p, commands.ChannelJoinBySnapshotStatus{ 1210 ClientAuth: n.ClientAuthRequired, 1211 }) 1212 Expect(err).NotTo(HaveOccurred()) 1213 Eventually(sess, n.EventuallyTimeout).Should(gexec.Exit(0)) 1214 return string(sess.Out.Contents()) 1215 } 1216 1217 // Cryptogen starts a gexec.Session for the provided cryptogen command. 1218 func (n *Network) Cryptogen(command Command) (*gexec.Session, error) { 1219 cmd := NewCommand(n.Components.Cryptogen(), command) 1220 return n.StartSession(cmd, command.SessionName()) 1221 } 1222 1223 // Idemixgen starts a gexec.Session for the provided idemixgen command. 1224 func (n *Network) Idemixgen(command Command) (*gexec.Session, error) { 1225 cmd := NewCommand(n.Components.Idemixgen(), command) 1226 return n.StartSession(cmd, command.SessionName()) 1227 } 1228 1229 // ConfigTxGen starts a gexec.Session for the provided configtxgen command. 1230 func (n *Network) ConfigTxGen(command Command) (*gexec.Session, error) { 1231 cmd := NewCommand(n.Components.ConfigTxGen(), command) 1232 return n.StartSession(cmd, command.SessionName()) 1233 } 1234 1235 // Discover starts a gexec.Session for the provided discover command. 1236 func (n *Network) Discover(command Command) (*gexec.Session, error) { 1237 cmd := NewCommand(n.Components.Discover(), command) 1238 cmd.Args = append(cmd.Args, "--peerTLSCA", n.CACertsBundlePath()) 1239 return n.StartSession(cmd, command.SessionName()) 1240 } 1241 1242 // Osnadmin starts a gexec.Session for the provided osnadmin command. 1243 func (n *Network) Osnadmin(command Command) (*gexec.Session, error) { 1244 cmd := NewCommand(n.Components.Osnadmin(), command) 1245 return n.StartSession(cmd, command.SessionName()) 1246 } 1247 1248 // ZooKeeperRunner returns a runner for a ZooKeeper instance. 1249 func (n *Network) ZooKeeperRunner(idx int) *runner.ZooKeeper { 1250 colorCode := n.nextColor() 1251 name := fmt.Sprintf("zookeeper-%d-%s", idx, n.NetworkID) 1252 1253 return &runner.ZooKeeper{ 1254 ZooMyID: idx + 1, // IDs must be between 1 and 255 1255 Client: n.DockerClient, 1256 Name: name, 1257 NetworkName: n.NetworkID, 1258 OutputStream: gexec.NewPrefixedWriter( 1259 fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1260 ginkgo.GinkgoWriter, 1261 ), 1262 ErrorStream: gexec.NewPrefixedWriter( 1263 fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1264 ginkgo.GinkgoWriter, 1265 ), 1266 } 1267 } 1268 1269 func (n *Network) minBrokersInSync() int { 1270 if n.Consensus.Brokers < 2 { 1271 return n.Consensus.Brokers 1272 } 1273 return 2 1274 } 1275 1276 func (n *Network) defaultBrokerReplication() int { 1277 if n.Consensus.Brokers < 3 { 1278 return n.Consensus.Brokers 1279 } 1280 return 3 1281 } 1282 1283 // BrokerRunner returns a runner for a Kafka broker instance. 1284 func (n *Network) BrokerRunner(id int, zookeepers []string) *runner.Kafka { 1285 colorCode := n.nextColor() 1286 name := fmt.Sprintf("kafka-%d-%s", id, n.NetworkID) 1287 1288 return &runner.Kafka{ 1289 BrokerID: id + 1, 1290 Client: n.DockerClient, 1291 AdvertisedListeners: "127.0.0.1", 1292 HostPort: int(n.PortsByBrokerID[strconv.Itoa(id)][HostPort]), 1293 Name: name, 1294 NetworkName: n.NetworkID, 1295 MinInsyncReplicas: n.minBrokersInSync(), 1296 DefaultReplicationFactor: n.defaultBrokerReplication(), 1297 ZooKeeperConnect: strings.Join(zookeepers, ","), 1298 OutputStream: gexec.NewPrefixedWriter( 1299 fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1300 ginkgo.GinkgoWriter, 1301 ), 1302 ErrorStream: gexec.NewPrefixedWriter( 1303 fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", colorCode, name), 1304 ginkgo.GinkgoWriter, 1305 ), 1306 } 1307 } 1308 1309 // BrokerGroupRunner returns a runner that manages the processes that make up 1310 // the Kafka broker network for fabric. 1311 func (n *Network) BrokerGroupRunner() ifrit.Runner { 1312 members := grouper.Members{} 1313 zookeepers := []string{} 1314 1315 for i := 0; i < n.Consensus.ZooKeepers; i++ { 1316 zk := n.ZooKeeperRunner(i) 1317 zookeepers = append(zookeepers, fmt.Sprintf("%s:2181", zk.Name)) 1318 members = append(members, grouper.Member{Name: zk.Name, Runner: zk}) 1319 } 1320 1321 for i := 0; i < n.Consensus.Brokers; i++ { 1322 kafka := n.BrokerRunner(i, zookeepers) 1323 members = append(members, grouper.Member{Name: kafka.Name, Runner: kafka}) 1324 } 1325 1326 return grouper.NewOrdered(syscall.SIGTERM, members) 1327 } 1328 1329 // OrdererRunner returns an ifrit.Runner for the specified orderer. The runner 1330 // can be used to start and manage an orderer process. 1331 func (n *Network) OrdererRunner(o *Orderer, env ...string) *ginkgomon.Runner { 1332 cmd := exec.Command(n.Components.Orderer()) 1333 cmd.Env = os.Environ() 1334 cmd.Env = append(cmd.Env, fmt.Sprintf("FABRIC_CFG_PATH=%s", n.OrdererDir(o))) 1335 cmd.Env = append(cmd.Env, env...) 1336 1337 config := ginkgomon.Config{ 1338 AnsiColorCode: n.nextColor(), 1339 Name: o.ID(), 1340 Command: cmd, 1341 StartCheck: "Beginning to serve requests", 1342 StartCheckTimeout: 15 * time.Second, 1343 } 1344 1345 // After consensus-type migration, the #brokers is >0, but the type is etcdraft 1346 if n.Consensus.Type == "kafka" && n.Consensus.Brokers != 0 { 1347 config.StartCheck = "Start phase completed successfully" 1348 config.StartCheckTimeout = 30 * time.Second 1349 } 1350 1351 return ginkgomon.New(config) 1352 } 1353 1354 // OrdererGroupRunner returns a runner that can be used to start and stop all 1355 // orderers in a network. 1356 func (n *Network) OrdererGroupRunner() ifrit.Runner { 1357 members := grouper.Members{} 1358 for _, o := range n.Orderers { 1359 members = append(members, grouper.Member{Name: o.ID(), Runner: n.OrdererRunner(o)}) 1360 } 1361 return grouper.NewParallel(syscall.SIGTERM, members) 1362 } 1363 1364 // PeerRunner returns an ifrit.Runner for the specified peer. The runner can be 1365 // used to start and manage a peer process. 1366 func (n *Network) PeerRunner(p *Peer, env ...string) *ginkgomon.Runner { 1367 cmd := n.peerCommand( 1368 commands.NodeStart{PeerID: p.ID(), DevMode: p.DevMode}, 1369 "", 1370 "FABRIC_CFG_PATH="+n.PeerDir(p), 1371 "CORE_LEDGER_STATE_COUCHDBCONFIG_USERNAME=admin", 1372 "CORE_LEDGER_STATE_COUCHDBCONFIG_PASSWORD=adminpw", 1373 ) 1374 cmd.Env = append(cmd.Env, env...) 1375 1376 return ginkgomon.New(ginkgomon.Config{ 1377 AnsiColorCode: n.nextColor(), 1378 Name: p.ID(), 1379 Command: cmd, 1380 StartCheck: `Started peer with ID=.*, .*, address=`, 1381 StartCheckTimeout: 15 * time.Second, 1382 }) 1383 } 1384 1385 // PeerGroupRunner returns a runner that can be used to start and stop all 1386 // peers in a network. 1387 func (n *Network) PeerGroupRunner() ifrit.Runner { 1388 members := grouper.Members{} 1389 for _, p := range n.Peers { 1390 members = append(members, grouper.Member{Name: p.ID(), Runner: n.PeerRunner(p)}) 1391 } 1392 return grouper.NewParallel(syscall.SIGTERM, members) 1393 } 1394 1395 // NetworkGroupRunner returns a runner that can be used to start and stop an 1396 // entire fabric network. 1397 func (n *Network) NetworkGroupRunner() ifrit.Runner { 1398 members := grouper.Members{ 1399 {Name: "brokers", Runner: n.BrokerGroupRunner()}, 1400 {Name: "orderers", Runner: n.OrdererGroupRunner()}, 1401 {Name: "peers", Runner: n.PeerGroupRunner()}, 1402 } 1403 return grouper.NewOrdered(syscall.SIGTERM, members) 1404 } 1405 1406 func (n *Network) peerCommand(command Command, tlsDir string, env ...string) *exec.Cmd { 1407 cmd := NewCommand(n.Components.Peer(), command) 1408 cmd.Env = append(cmd.Env, env...) 1409 1410 if connectsToOrderer(command) && n.TLSEnabled { 1411 cmd.Args = append(cmd.Args, "--tls") 1412 cmd.Args = append(cmd.Args, "--cafile", n.CACertsBundlePath()) 1413 } 1414 1415 if clientAuthEnabled(command) { 1416 certfilePath := filepath.Join(tlsDir, "client.crt") 1417 keyfilePath := filepath.Join(tlsDir, "client.key") 1418 1419 cmd.Args = append(cmd.Args, "--certfile", certfilePath) 1420 cmd.Args = append(cmd.Args, "--keyfile", keyfilePath) 1421 } 1422 1423 // In case we have a peer invoke with multiple certificates, we need to mimic 1424 // the correct peer CLI usage, so we count the number of --peerAddresses 1425 // usages we have, and add the same (concatenated TLS CA certificates file) 1426 // the same number of times to bypass the peer CLI sanity checks 1427 requiredPeerAddresses := flagCount("--peerAddresses", cmd.Args) 1428 for i := 0; i < requiredPeerAddresses; i++ { 1429 cmd.Args = append(cmd.Args, "--tlsRootCertFiles") 1430 cmd.Args = append(cmd.Args, n.CACertsBundlePath()) 1431 } 1432 1433 // If there is --peerAddress, add --tlsRootCertFile parameter 1434 requiredPeerAddress := flagCount("--peerAddress", cmd.Args) 1435 if requiredPeerAddress > 0 { 1436 cmd.Args = append(cmd.Args, "--tlsRootCertFile") 1437 cmd.Args = append(cmd.Args, n.CACertsBundlePath()) 1438 } 1439 return cmd 1440 } 1441 1442 func connectsToOrderer(c Command) bool { 1443 for _, arg := range c.Args() { 1444 if arg == "--orderer" { 1445 return true 1446 } 1447 } 1448 return false 1449 } 1450 1451 func clientAuthEnabled(c Command) bool { 1452 for _, arg := range c.Args() { 1453 if arg == "--clientauth" { 1454 return true 1455 } 1456 } 1457 return false 1458 } 1459 1460 func flagCount(flag string, args []string) int { 1461 var c int 1462 for _, arg := range args { 1463 if arg == flag { 1464 c++ 1465 } 1466 } 1467 return c 1468 } 1469 1470 // PeerAdminSession starts a gexec.Session as a peer admin for the provided 1471 // peer command. This is intended to be used by short running peer cli commands 1472 // that execute in the context of a peer configuration. 1473 func (n *Network) PeerAdminSession(p *Peer, command Command) (*gexec.Session, error) { 1474 return n.PeerUserSession(p, "Admin", command) 1475 } 1476 1477 // PeerUserSession starts a gexec.Session as a peer user for the provided peer 1478 // command. This is intended to be used by short running peer cli commands that 1479 // execute in the context of a peer configuration. 1480 func (n *Network) PeerUserSession(p *Peer, user string, command Command) (*gexec.Session, error) { 1481 cmd := n.peerCommand( 1482 command, 1483 n.PeerUserTLSDir(p, user), 1484 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1485 fmt.Sprintf("CORE_PEER_MSPCONFIGPATH=%s", n.PeerUserMSPDir(p, user)), 1486 ) 1487 return n.StartSession(cmd, command.SessionName()) 1488 } 1489 1490 // PeerClientConn returns a grpc.ClientConn configured to connect to the 1491 // provided peer. This connection can be used to create clients for the peer 1492 // services. The client connection should be closed when the tests are done 1493 // using it. 1494 func (n *Network) PeerClientConn(p *Peer) *grpc.ClientConn { 1495 return n.newClientConn( 1496 n.PeerAddress(p, ListenPort), 1497 filepath.Join(n.PeerLocalTLSDir(p), "ca.crt"), 1498 ) 1499 } 1500 1501 // OrdererClientConn returns a grpc.ClientConn configured to connect to the 1502 // provided orderer. This connection can be used to create clients for the 1503 // orderer services. The client connection should be closed when the tests are 1504 // done using it. 1505 func (n *Network) OrdererClientConn(o *Orderer) *grpc.ClientConn { 1506 return n.newClientConn( 1507 n.OrdererAddress(o, ListenPort), 1508 filepath.Join(n.OrdererLocalTLSDir(o), "ca.crt"), 1509 ) 1510 } 1511 1512 func (n *Network) newClientConn(address, ca string) *grpc.ClientConn { 1513 fingerprint := "grpc::" + address + "::" + ca 1514 if d := n.throttleDuration(fingerprint); d > 0 { 1515 time.Sleep(d) 1516 } 1517 1518 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 1519 defer cancel() 1520 1521 creds, err := credentials.NewClientTLSFromFile(ca, "") 1522 Expect(err).NotTo(HaveOccurred()) 1523 1524 conn, err := grpc.DialContext( 1525 ctx, 1526 address, 1527 grpc.WithBlock(), 1528 grpc.FailOnNonTempDialError(true), 1529 grpc.WithTransportCredentials(creds), 1530 ) 1531 Expect(err).NotTo(HaveOccurred()) 1532 1533 return conn 1534 } 1535 1536 // IdemixUserSession starts a gexec.Session as a idemix user for the provided peer 1537 // command. This is intended to be used by short running peer cli commands that 1538 // execute in the context of a peer configuration. 1539 func (n *Network) IdemixUserSession(p *Peer, idemixOrg *Organization, user string, command Command) (*gexec.Session, error) { 1540 cmd := n.peerCommand( 1541 command, 1542 n.PeerUserTLSDir(p, user), 1543 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1544 fmt.Sprintf("CORE_PEER_MSPCONFIGPATH=%s", n.IdemixUserMSPDir(idemixOrg, user)), 1545 fmt.Sprintf("CORE_PEER_LOCALMSPTYPE=%s", "idemix"), 1546 fmt.Sprintf("CORE_PEER_LOCALMSPID=%s", idemixOrg.MSPID), 1547 ) 1548 return n.StartSession(cmd, command.SessionName()) 1549 } 1550 1551 // OrdererAdminSession starts a gexec.Session as an orderer admin user. This 1552 // is used primarily to generate orderer configuration updates. 1553 func (n *Network) OrdererAdminSession(o *Orderer, p *Peer, command Command) (*gexec.Session, error) { 1554 cmd := n.peerCommand( 1555 command, 1556 n.ordererUserCryptoDir(o, "Admin", "tls"), 1557 fmt.Sprintf("CORE_PEER_LOCALMSPID=%s", n.Organization(o.Organization).MSPID), 1558 fmt.Sprintf("FABRIC_CFG_PATH=%s", n.PeerDir(p)), 1559 fmt.Sprintf("CORE_PEER_MSPCONFIGPATH=%s", n.OrdererUserMSPDir(o, "Admin")), 1560 ) 1561 return n.StartSession(cmd, command.SessionName()) 1562 } 1563 1564 // Peer returns the information about the named Peer in the named organization. 1565 func (n *Network) Peer(orgName, peerName string) *Peer { 1566 for _, p := range n.PeersInOrg(orgName) { 1567 if p.Name == peerName { 1568 return p 1569 } 1570 } 1571 return nil 1572 } 1573 1574 // DiscoveredPeer creates a new DiscoveredPeer from the peer and chaincodes 1575 // passed as arguments. 1576 func (n *Network) DiscoveredPeer(p *Peer, chaincodes ...string) DiscoveredPeer { 1577 peerCert, err := ioutil.ReadFile(n.PeerCert(p)) 1578 Expect(err).NotTo(HaveOccurred()) 1579 1580 return DiscoveredPeer{ 1581 MSPID: n.Organization(p.Organization).MSPID, 1582 Endpoint: fmt.Sprintf("127.0.0.1:%d", n.PeerPort(p, ListenPort)), 1583 Identity: string(peerCert), 1584 Chaincodes: chaincodes, 1585 } 1586 } 1587 1588 // Orderer returns the information about the named Orderer. 1589 func (n *Network) Orderer(name string) *Orderer { 1590 for _, o := range n.Orderers { 1591 if o.Name == name { 1592 return o 1593 } 1594 } 1595 return nil 1596 } 1597 1598 // Organization returns the information about the named Organization. 1599 func (n *Network) Organization(orgName string) *Organization { 1600 for _, org := range n.Organizations { 1601 if org.Name == orgName { 1602 return org 1603 } 1604 } 1605 return nil 1606 } 1607 1608 // Consortium returns information about the named Consortium. 1609 func (n *Network) Consortium(name string) *Consortium { 1610 for _, c := range n.Consortiums { 1611 if c.Name == name { 1612 return c 1613 } 1614 } 1615 return nil 1616 } 1617 1618 // PeerOrgs returns all Organizations associated with at least one Peer. 1619 func (n *Network) PeerOrgs() []*Organization { 1620 orgsByName := map[string]*Organization{} 1621 for _, p := range n.Peers { 1622 if n.Organization(p.Organization).MSPType != "idemix" { 1623 orgsByName[p.Organization] = n.Organization(p.Organization) 1624 } 1625 } 1626 1627 orgs := []*Organization{} 1628 for _, org := range orgsByName { 1629 orgs = append(orgs, org) 1630 } 1631 return orgs 1632 } 1633 1634 // IdemixOrgs returns all Organizations of type idemix. 1635 func (n *Network) IdemixOrgs() []*Organization { 1636 orgs := []*Organization{} 1637 for _, org := range n.Organizations { 1638 if org.MSPType == "idemix" { 1639 orgs = append(orgs, org) 1640 } 1641 } 1642 return orgs 1643 } 1644 1645 // PeersWithChannel returns all Peer instances that have joined the named 1646 // channel. 1647 func (n *Network) PeersWithChannel(chanName string) []*Peer { 1648 peers := []*Peer{} 1649 for _, p := range n.Peers { 1650 for _, c := range p.Channels { 1651 if c.Name == chanName { 1652 peers = append(peers, p) 1653 } 1654 } 1655 } 1656 1657 // This is a bit of a hack to make the output of this function deterministic. 1658 // When this function's output is supplied as input to functions such as ApproveChaincodeForMyOrg 1659 // it causes a different subset of peers to be picked, which can create flakiness in tests. 1660 sort.Slice(peers, func(i, j int) bool { 1661 if peers[i].Organization < peers[j].Organization { 1662 return true 1663 } 1664 1665 return peers[i].Organization == peers[j].Organization && peers[i].Name < peers[j].Name 1666 }) 1667 return peers 1668 } 1669 1670 // AnchorsForChannel returns all Peer instances that are anchors for the 1671 // named channel. 1672 func (n *Network) AnchorsForChannel(chanName string) []*Peer { 1673 anchors := []*Peer{} 1674 for _, p := range n.Peers { 1675 for _, pc := range p.Channels { 1676 if pc.Name == chanName && pc.Anchor { 1677 anchors = append(anchors, p) 1678 } 1679 } 1680 } 1681 return anchors 1682 } 1683 1684 // AnchorsInOrg returns all peers that are an anchor for at least one channel 1685 // in the named organization. 1686 func (n *Network) AnchorsInOrg(orgName string) []*Peer { 1687 anchors := []*Peer{} 1688 for _, p := range n.PeersInOrg(orgName) { 1689 if p.Anchor() { 1690 anchors = append(anchors, p) 1691 break 1692 } 1693 } 1694 1695 // No explicit anchor means all peers are anchors. 1696 if len(anchors) == 0 { 1697 anchors = n.PeersInOrg(orgName) 1698 } 1699 1700 return anchors 1701 } 1702 1703 // OrderersInOrg returns all Orderer instances owned by the named organization. 1704 func (n *Network) OrderersInOrg(orgName string) []*Orderer { 1705 orderers := []*Orderer{} 1706 for _, o := range n.Orderers { 1707 if o.Organization == orgName { 1708 orderers = append(orderers, o) 1709 } 1710 } 1711 return orderers 1712 } 1713 1714 // OrgsForOrderers returns all Organization instances that own at least one of 1715 // the named orderers. 1716 func (n *Network) OrgsForOrderers(ordererNames []string) []*Organization { 1717 orgsByName := map[string]*Organization{} 1718 for _, name := range ordererNames { 1719 orgName := n.Orderer(name).Organization 1720 orgsByName[orgName] = n.Organization(orgName) 1721 } 1722 orgs := []*Organization{} 1723 for _, org := range orgsByName { 1724 orgs = append(orgs, org) 1725 } 1726 return orgs 1727 } 1728 1729 // OrdererOrgs returns all Organization instances that own at least one 1730 // orderer. 1731 func (n *Network) OrdererOrgs() []*Organization { 1732 orgsByName := map[string]*Organization{} 1733 for _, o := range n.Orderers { 1734 orgsByName[o.Organization] = n.Organization(o.Organization) 1735 } 1736 1737 orgs := []*Organization{} 1738 for _, org := range orgsByName { 1739 orgs = append(orgs, org) 1740 } 1741 return orgs 1742 } 1743 1744 // PeersInOrg returns all Peer instances that are owned by the named 1745 // organization. 1746 func (n *Network) PeersInOrg(orgName string) []*Peer { 1747 peers := []*Peer{} 1748 for _, o := range n.Peers { 1749 if o.Organization == orgName { 1750 peers = append(peers, o) 1751 } 1752 } 1753 return peers 1754 } 1755 1756 // ReservePort allocates the next available port. 1757 func (n *Network) ReservePort() uint16 { 1758 n.StartPort++ 1759 return n.StartPort - 1 1760 } 1761 1762 type ( 1763 PortName string 1764 Ports map[PortName]uint16 1765 ) 1766 1767 const ( 1768 ChaincodePort PortName = "Chaincode" 1769 EventsPort PortName = "Events" 1770 HostPort PortName = "HostPort" 1771 ListenPort PortName = "Listen" 1772 ProfilePort PortName = "Profile" 1773 OperationsPort PortName = "Operations" 1774 ClusterPort PortName = "Cluster" 1775 AdminPort PortName = "Admin" 1776 ) 1777 1778 // PeerPortNames returns the list of ports that need to be reserved for a Peer. 1779 func PeerPortNames() []PortName { 1780 return []PortName{ListenPort, ChaincodePort, EventsPort, ProfilePort, OperationsPort} 1781 } 1782 1783 // OrdererPortNames returns the list of ports that need to be reserved for an 1784 // Orderer. 1785 func OrdererPortNames() []PortName { 1786 return []PortName{ListenPort, ProfilePort, OperationsPort, ClusterPort, AdminPort} 1787 } 1788 1789 // BrokerPortNames returns the list of ports that need to be reserved for a 1790 // Kafka broker. 1791 func BrokerPortNames() []PortName { 1792 return []PortName{HostPort} 1793 } 1794 1795 // BrokerAddresses returns the list of broker addresses for the network. 1796 func (n *Network) BrokerAddresses(portName PortName) []string { 1797 addresses := []string{} 1798 for _, ports := range n.PortsByBrokerID { 1799 addresses = append(addresses, fmt.Sprintf("127.0.0.1:%d", ports[portName])) 1800 } 1801 return addresses 1802 } 1803 1804 // OrdererAddress returns the address (host and port) exposed by the Orderer 1805 // for the named port. Command line tools should use the returned address when 1806 // connecting to the orderer. 1807 // 1808 // This assumes that the orderer is listening on 0.0.0.0 or 127.0.0.1 and is 1809 // available on the loopback address. 1810 func (n *Network) OrdererAddress(o *Orderer, portName PortName) string { 1811 return fmt.Sprintf("127.0.0.1:%d", n.OrdererPort(o, portName)) 1812 } 1813 1814 // OrdererPort returns the named port reserved for the Orderer instance. 1815 func (n *Network) OrdererPort(o *Orderer, portName PortName) uint16 { 1816 ordererPorts := n.PortsByOrdererID[o.ID()] 1817 Expect(ordererPorts).NotTo(BeNil()) 1818 return ordererPorts[portName] 1819 } 1820 1821 // PeerAddress returns the address (host and port) exposed by the Peer for the 1822 // named port. Command line tools should use the returned address when 1823 // connecting to a peer. 1824 // 1825 // This assumes that the peer is listening on 0.0.0.0 and is available on the 1826 // loopback address. 1827 func (n *Network) PeerAddress(p *Peer, portName PortName) string { 1828 return fmt.Sprintf("127.0.0.1:%d", n.PeerPort(p, portName)) 1829 } 1830 1831 // PeerPort returns the named port reserved for the Peer instance. 1832 func (n *Network) PeerPort(p *Peer, portName PortName) uint16 { 1833 peerPorts := n.PortsByPeerID[p.ID()] 1834 Expect(peerPorts).NotTo(BeNil()) 1835 return peerPorts[portName] 1836 } 1837 1838 func (n *Network) nextColor() string { 1839 n.mutex.Lock() 1840 defer n.mutex.Unlock() 1841 color := n.colorIndex%14 + 31 1842 if color > 37 { 1843 color = color + 90 - 37 1844 } 1845 1846 n.colorIndex++ 1847 return fmt.Sprintf("%dm", color) 1848 } 1849 1850 // StartSession executes a command session. This should be used to launch 1851 // command line tools that are expected to run to completion. 1852 func (n *Network) StartSession(cmd *exec.Cmd, name string) (*gexec.Session, error) { 1853 if d := n.throttleDuration(commandFingerprint(cmd)); d > 0 { 1854 time.Sleep(d) 1855 } 1856 1857 ansiColorCode := n.nextColor() 1858 fmt.Fprintf( 1859 ginkgo.GinkgoWriter, 1860 "\x1b[33m[d]\x1b[%s[%s]\x1b[0m starting %s %s\n", 1861 ansiColorCode, 1862 name, 1863 filepath.Base(cmd.Args[0]), 1864 strings.Join(cmd.Args[1:], " "), 1865 ) 1866 return gexec.Start( 1867 cmd, 1868 gexec.NewPrefixedWriter( 1869 fmt.Sprintf("\x1b[32m[o]\x1b[%s[%s]\x1b[0m ", ansiColorCode, name), 1870 ginkgo.GinkgoWriter, 1871 ), 1872 gexec.NewPrefixedWriter( 1873 fmt.Sprintf("\x1b[91m[e]\x1b[%s[%s]\x1b[0m ", ansiColorCode, name), 1874 ginkgo.GinkgoWriter, 1875 ), 1876 ) 1877 } 1878 1879 // commandFingerprint creates a string containing the program, args, and environment 1880 // of an exec.Cmd. This fingerprint is used to identify repeated calls to the 1881 // same command for throttling. 1882 func commandFingerprint(cmd *exec.Cmd) string { 1883 buf := bytes.NewBuffer(nil) 1884 _, err := buf.WriteString(cmd.Dir) 1885 Expect(err).NotTo(HaveOccurred()) 1886 _, err = buf.WriteString(cmd.Path) 1887 Expect(err).NotTo(HaveOccurred()) 1888 1889 // sort the environment since it's not positional 1890 env := append([]string(nil), cmd.Env...) 1891 sort.Strings(env) 1892 for _, e := range env { 1893 _, err := buf.WriteString(e) 1894 Expect(err).NotTo(HaveOccurred()) 1895 } 1896 1897 // grab the args but ignore references to temporary files 1898 for _, arg := range cmd.Args { 1899 if strings.HasPrefix(arg, os.TempDir()) { 1900 continue 1901 } 1902 _, err := buf.WriteString(arg) 1903 Expect(err).NotTo(HaveOccurred()) 1904 } 1905 1906 return buf.String() 1907 } 1908 1909 // throttleDuration returns the time to wait before performing some operation 1910 // representted by the fingerprint. 1911 // 1912 // The duration is determined by looking at when the fingerprinted operation 1913 // was last executed by the network. If the time between now and the last 1914 // execution was less than SessionCreateInterval, the difference between now 1915 // and (execution + SessionCreateInterval) is returned. If more than 1916 // SessionCreateInterval has elapsed since the last command execution, a 1917 // duration of 0 is returned. 1918 func (n *Network) throttleDuration(fingerprint string) time.Duration { 1919 now := time.Now() 1920 n.mutex.Lock() 1921 last := n.lastExecuted[fingerprint] 1922 n.lastExecuted[fingerprint] = now 1923 n.mutex.Unlock() 1924 1925 if diff := last.Add(n.SessionCreateInterval).Sub(now); diff > 0 { 1926 return diff 1927 } 1928 1929 return 0 1930 } 1931 1932 // GenerateCryptoConfig creates the `crypto-config.yaml` configuration file 1933 // provided to `cryptogen` when running Bootstrap. The path to the generated 1934 // file can be obtained from CryptoConfigPath. 1935 func (n *Network) GenerateCryptoConfig() { 1936 crypto, err := os.Create(n.CryptoConfigPath()) 1937 Expect(err).NotTo(HaveOccurred()) 1938 defer crypto.Close() 1939 1940 t, err := template.New("crypto").Parse(n.Templates.CryptoTemplate()) 1941 Expect(err).NotTo(HaveOccurred()) 1942 1943 pw := gexec.NewPrefixedWriter("[crypto-config.yaml] ", ginkgo.GinkgoWriter) 1944 err = t.Execute(io.MultiWriter(crypto, pw), n) 1945 Expect(err).NotTo(HaveOccurred()) 1946 } 1947 1948 // GenerateConfigTxConfig creates the `configtx.yaml` configuration file 1949 // provided to `configtxgen` when running Bootstrap. The path to the generated 1950 // file can be obtained from ConfigTxConfigPath. 1951 func (n *Network) GenerateConfigTxConfig() { 1952 config, err := os.Create(n.ConfigTxConfigPath()) 1953 Expect(err).NotTo(HaveOccurred()) 1954 defer config.Close() 1955 1956 t, err := template.New("configtx").Parse(n.Templates.ConfigTxTemplate()) 1957 Expect(err).NotTo(HaveOccurred()) 1958 1959 pw := gexec.NewPrefixedWriter("[configtx.yaml] ", ginkgo.GinkgoWriter) 1960 err = t.Execute(io.MultiWriter(config, pw), n) 1961 Expect(err).NotTo(HaveOccurred()) 1962 } 1963 1964 // GenerateOrdererConfig creates the `orderer.yaml` configuration file for the 1965 // specified orderer. The path to the generated file can be obtained from 1966 // OrdererConfigPath. 1967 func (n *Network) GenerateOrdererConfig(o *Orderer) { 1968 err := os.MkdirAll(n.OrdererDir(o), 0o755) 1969 Expect(err).NotTo(HaveOccurred()) 1970 1971 orderer, err := os.Create(n.OrdererConfigPath(o)) 1972 Expect(err).NotTo(HaveOccurred()) 1973 defer orderer.Close() 1974 1975 t, err := template.New("orderer").Funcs(template.FuncMap{ 1976 "Orderer": func() *Orderer { return o }, 1977 "ToLower": func(s string) string { return strings.ToLower(s) }, 1978 "ReplaceAll": func(s, old, new string) string { return strings.Replace(s, old, new, -1) }, 1979 }).Parse(n.Templates.OrdererTemplate()) 1980 Expect(err).NotTo(HaveOccurred()) 1981 1982 pw := gexec.NewPrefixedWriter(fmt.Sprintf("[%s#orderer.yaml] ", o.ID()), ginkgo.GinkgoWriter) 1983 err = t.Execute(io.MultiWriter(orderer, pw), n) 1984 Expect(err).NotTo(HaveOccurred()) 1985 } 1986 1987 // GenerateCoreConfig creates the `core.yaml` configuration file for the 1988 // specified peer. The path to the generated file can be obtained from 1989 // PeerConfigPath. 1990 func (n *Network) GenerateCoreConfig(p *Peer) { 1991 err := os.MkdirAll(n.PeerDir(p), 0o755) 1992 Expect(err).NotTo(HaveOccurred()) 1993 1994 core, err := os.Create(n.PeerConfigPath(p)) 1995 Expect(err).NotTo(HaveOccurred()) 1996 defer core.Close() 1997 1998 t, err := template.New("peer").Funcs(template.FuncMap{ 1999 "Peer": func() *Peer { return p }, 2000 "ToLower": func(s string) string { return strings.ToLower(s) }, 2001 "ReplaceAll": func(s, old, new string) string { return strings.Replace(s, old, new, -1) }, 2002 }).Parse(n.Templates.CoreTemplate()) 2003 Expect(err).NotTo(HaveOccurred()) 2004 2005 pw := gexec.NewPrefixedWriter(fmt.Sprintf("[%s#core.yaml] ", p.ID()), ginkgo.GinkgoWriter) 2006 err = t.Execute(io.MultiWriter(core, pw), n) 2007 Expect(err).NotTo(HaveOccurred()) 2008 }