-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1275cf2
Showing
215 changed files
with
51,496 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
.idea |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# HLF Easy | ||
|
||
HLF Easy is a tool to simplify the process of setting up Hyperledger Fabric nodes. It is designed to be used in baremetal environment. | ||
|
||
![Easy HLF.png](./docs/images/Easy%20HLF.png) | ||
|
||
# Tutorial | ||
|
||
### Pre requisites | ||
- HLF Peer binaries | ||
- HLF Orderer binaries | ||
|
||
|
||
|
||
### Install fabric binaries | ||
|
||
```bash | ||
curl -sSLO https://raw.githubusercontent.com/hyperledger/fabric/main/scripts/install-fabric.sh && chmod +x install-fabric.sh | ||
./install-fabric.sh 2.5.5 | ||
``` | ||
|
||
### Compile the code | ||
```bash | ||
go build -o hlf-easy ./main.go && sudo mv hlf-easy /usr/local/bin/hlf-easy | ||
``` | ||
|
||
### Follow the HLF meetup 2024 | ||
|
||
Complete the meetup 2024 to get a running network with 2 peer organizations and 1 orderer organization. | ||
|
||
Repository: https://github.com/kfsoftware/meetup-k8s-hlf-2024 | ||
|
||
Youtube: https://www.youtube.com/watch?v=8qPXaRzrFiQ | ||
|
||
### Init the certificate authority | ||
|
||
After initializing the certificate authority, you can inspect the certificate authority to get the certificates. | ||
|
||
All the certificates will be saved in $HOME/hlf-easy with a custom structure. | ||
|
||
```bash | ||
|
||
hlf-easy ca init --hosts=192.168.1.36 --hosts localhost --hosts 127.0.0.1 --hosts ca.localho.st --name=ca-1 | ||
|
||
hlf-easy ca inspect --name=ca-1 > localorg1-ca.yaml | ||
|
||
``` | ||
|
||
### Initializing the peer certificates | ||
|
||
Once we have the certificates generated we need to initialize the peer certificates. | ||
|
||
First, we need to find our external IP address, this is the IP address that the network nodes will use to connect to the peer. | ||
|
||
You can use `ifconfig` or `ip a` to find the IP address of the interface that is connected to the network. | ||
```bash | ||
export EXTERNAL_HOST=192.168.1.36 | ||
``` | ||
|
||
And then, we can initialize the peer certificates. | ||
```bash | ||
hlf-easy peer init --hosts=${EXTERNAL_HOST} --hosts localhost --hosts 127.0.0.1 --hosts peer01.localho.st --ca-name=ca-1 --id=peer1 --local=true | ||
|
||
hlf-easy peer init --hosts=${EXTERNAL_HOST} --hosts localhost --hosts 127.0.0.1 --hosts peer02.localho.st --ca-name=ca-1 --id=peer2 --local=true | ||
``` | ||
|
||
### Starting the peers | ||
|
||
```bash | ||
|
||
hlf-easy peer start --id=peer1 --msp-id=LocalOrg1 --external-endpoint="${EXTERNAL_HOST}:7051" \ | ||
--listen-address="0.0.0.0:7051" \ | ||
--chaincode-address="0.0.0.0:7052" \ | ||
--events-address="0.0.0.0:7053" \ | ||
--operations-listen-address="0.0.0.0:7054" \ | ||
--mgmt-address="0.0.0.0:7055" | ||
|
||
|
||
hlf-easy peer start --id=peer2 --msp-id=LocalOrg1 --external-endpoint="${EXTERNAL_HOST}:7061" \ | ||
--listen-address="0.0.0.0:7061" \ | ||
--chaincode-address="0.0.0.0:7062" \ | ||
--events-address="0.0.0.0:7063" \ | ||
--operations-listen-address="0.0.0.0:7064" \ | ||
--mgmt-address="0.0.0.0:7065" | ||
``` | ||
### Enroll the admin and client | ||
|
||
After the peer is started, we can enroll the admin and client using our local ca | ||
```bash | ||
|
||
hlf-easy ca enroll --name=ca-1 --local=true --type=admin --common-name=admin > peer-admin.yaml | ||
|
||
hlf-easy ca enroll --name=ca-1 --local=true --type=client --common-name=client > peer-client.yaml | ||
``` | ||
|
||
### Joining a network | ||
|
||
Once the peer is started and the admin is enrolled, we can join the peer to a network, for this, we need to have a running network, the variables to get the orderer certificate and the URLs are based on the 2024 HLF workshop mentioned above. | ||
|
||
```bash | ||
|
||
kubectl get fabricorderernodes ord-node1 -o=jsonpath='{.status.tlsCert}' | sed -e "s/^/${IDENT_8}/" > orderer0-tls.pem | ||
|
||
hlf-easy peer join --id=peer1 --channel=demo2 --identity=peer-admin.yaml --orderer-url=grpcs://orderer0-ord.localho.st:443 --orderer-tls-cert=orderer0-tls.pem | ||
|
||
hlf-easy peer join --id=peer2 --channel=demo2 --identity=peer-admin.yaml --orderer-url=grpcs://orderer0-ord.localho.st:443 --orderer-tls-cert=orderer0-tls.pem | ||
``` | ||
### Setting the anchor peers | ||
```bash | ||
hlf-easy peer anchorpeers set --id=peer1 --channel=demo2 --identity=peer-admin.yaml \ | ||
--orderer-url=grpcs://orderer0-ord.localho.st:443 --orderer-tls-cert=orderer0-tls.pem \ | ||
--anchor-peers="${EXTERNAL_HOST}:7051" --anchor-peers="${EXTERNAL_HOST}:7061" | ||
|
||
|
||
|
||
``` | ||
|
||
## Roadmap | ||
|
||
- [ ] Enroll using Fabric CA instead of local CA | ||
- [ ] Docs for setting up and joining orderers | ||
- [ ] Admin UI for Peer | ||
- [ ] Add detail of the peer + process stats (CPU, memory, requests) | ||
- [ ] Add logs | ||
- [ ] Add chaincode support | ||
- [ ] Add operations: restart, stop, start, upgrade, certificate renewal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
package api | ||
|
||
import ( | ||
"embed" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/gin-contrib/cors" | ||
"github.com/gin-contrib/static" | ||
"github.com/gin-gonic/gin" | ||
"github.com/hyperledger/fabric-lib-go/healthz" | ||
"github.com/hyperledger/fabric/core/operations" | ||
"hlf-easy/config" | ||
"hlf-easy/node" | ||
"hlf-easy/ui" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
"path/filepath" | ||
) | ||
|
||
type OrdererClient struct { | ||
OperationsAddress string | ||
OrdererAddress string | ||
} | ||
|
||
func (pc *OrdererClient) GetVersionInfo() (*operations.VersionInfoHandler, error) { | ||
resp, err := http.Get(pc.OperationsAddress + "/version") | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
versionInfo := &operations.VersionInfoHandler{} | ||
err = json.Unmarshal(body, versionInfo) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return versionInfo, nil | ||
} | ||
|
||
func (pc *OrdererClient) GetHealthz() (*healthz.HealthStatus, error) { | ||
resp, err := http.Get(pc.OperationsAddress + "/healthz") | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
body, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
healthInfo := &healthz.HealthStatus{} | ||
err = json.Unmarshal(body, healthInfo) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return healthInfo, nil | ||
} | ||
|
||
func getHandlerFuncForOrdererFile(opts config.StartOrdererOpts, filename string) func(c *gin.Context) { | ||
return func(c *gin.Context) { | ||
contents, err := os.ReadFile(filepath.Join(opts.MSPConfigPath, filename)) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
c.JSON(http.StatusOK, gin.H{ | ||
"contents": string(contents), | ||
}) | ||
} | ||
} | ||
|
||
func NewOrdererRouter( | ||
node *node.OrdererNode, | ||
cmdOrdererStdout *config.SaveOutputWriter, | ||
cmdOrdererStderr *config.SaveOutputWriter, | ||
startOptions config.OrdererStartOptions, | ||
opts config.StartOrdererOpts, | ||
views embed.FS, | ||
) (*gin.Engine, error) { | ||
r := gin.Default() | ||
peerClient := &OrdererClient{ | ||
OperationsAddress: fmt.Sprintf("http://%s", startOptions.OperationsListenAddress), | ||
OrdererAddress: startOptions.ListenAddress, | ||
} | ||
config := cors.DefaultConfig() | ||
config.AllowAllOrigins = true // Allow all origins | ||
config.AllowMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"} // Specify what methods are allowed | ||
config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type"} | ||
|
||
r.Use(cors.New(config)) | ||
r.GET("/tls.crt", getHandlerFuncForOrdererFile(opts, "tls.crt")) | ||
r.GET("/tlscacert.crt", getHandlerFuncForOrdererFile(opts, "tlscacerts/cacert.pem")) | ||
r.GET("/cacert.crt", getHandlerFuncForOrdererFile(opts, "cacerts/cacert.pem")) | ||
r.GET("/sign.crt", getHandlerFuncForOrdererFile(opts, "signcerts/cert.pem")) | ||
r.GET("/core.yaml", getHandlerFuncForOrdererFile(opts, "core.yaml")) | ||
r.POST("/restart", func(context *gin.Context) { | ||
err := node.Stop() | ||
if err != nil { | ||
context.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
err = node.Start() | ||
if err != nil { | ||
context.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
context.JSON(http.StatusOK, gin.H{ | ||
"success": true, | ||
}) | ||
}) | ||
r.POST("/stop", func(context *gin.Context) { | ||
err := node.Stop() | ||
if err != nil { | ||
context.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
context.JSON(http.StatusOK, gin.H{ | ||
"success": true, | ||
}) | ||
}) | ||
r.POST("/start", func(context *gin.Context) { | ||
err := node.Start() | ||
if err != nil { | ||
context.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
context.JSON(http.StatusOK, gin.H{ | ||
"success": true, | ||
}) | ||
}) | ||
r.GET("/status", func(context *gin.Context) { | ||
status, err := node.Status() | ||
if err != nil { | ||
context.JSON(http.StatusBadRequest, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
context.JSON(http.StatusOK, status) | ||
}) | ||
r.GET("/config", func(c *gin.Context) { | ||
conf, err := node.GetConfig() | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
status, err := node.Status() | ||
if err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
version, err := peerClient.GetVersionInfo() | ||
if err != nil { | ||
// return generic error in this gin route | ||
version = &operations.VersionInfoHandler{ | ||
Version: "unknown", | ||
CommitSHA: "unknown", | ||
} | ||
} | ||
c.JSON(http.StatusOK, gin.H{ | ||
"config": conf, | ||
"status": status, | ||
"version": version, | ||
"startOptions": startOptions, | ||
}) | ||
}) | ||
r.GET("/logs", func(c *gin.Context) { | ||
c.JSON(http.StatusOK, gin.H{ | ||
"stdout": string(cmdOrdererStdout.GetSavedOutput()), | ||
"stderr": string(cmdOrdererStderr.GetSavedOutput()), | ||
}) | ||
}) | ||
fileSystem := ui.NewFileSystemUI(views, "web") | ||
|
||
r.GET("/healthz", func(c *gin.Context) { | ||
version, err := peerClient.GetHealthz() | ||
if err != nil { | ||
// return generic error in this gin route | ||
c.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
c.JSON(http.StatusOK, version) | ||
}) | ||
|
||
r.GET("/version", func(c *gin.Context) { | ||
version, err := peerClient.GetVersionInfo() | ||
if err != nil { | ||
// return generic error in this gin route | ||
c.JSON(http.StatusInternalServerError, gin.H{ | ||
"error": err.Error(), | ||
}) | ||
return | ||
} | ||
c.JSON(http.StatusOK, version) | ||
}) | ||
r.Use(static.Serve("/", fileSystem)) | ||
r.NoRoute(ReturnPublic(views)) | ||
return r, nil | ||
} |
Oops, something went wrong.