diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 979dabb..89d6505 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,7 +27,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: 1.23 - name: Build run: make build diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cb726f2..c5aad7a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: 1.23 - name: Run golangci-lint uses: golangci/golangci-lint-action@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f57a240..c931879 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.21 + go-version: 1.23 - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6.2.1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 64c4850..901581f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.23" - name: Check out code into the Go module directory uses: actions/checkout@v4 diff --git a/go.work b/go.work index f329111..179b9fc 100644 --- a/go.work +++ b/go.work @@ -1,3 +1,4 @@ -go 1.21.0 +go 1.23.0 use ./node-registrar +use ./rmb-sdk-go diff --git a/go.work.sum b/go.work.sum index e7d424d..aee883d 100644 --- a/go.work.sum +++ b/go.work.sum @@ -6,16 +6,40 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/vedhavyas/go-subkey v1.0.3 h1:iKR33BB/akKmcR2PMlXPBeeODjWLM90EL98OrOGs8CA= -github.com/vedhavyas/go-subkey v1.0.3/go.mod h1:CloUaFQSSTdWnINfBRFjVMkWXZANW+nd8+TI5jYcl6Y= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/node-registrar/Dockerfile b/node-registrar/Dockerfile index ba5d98e..e56a070 100644 --- a/node-registrar/Dockerfile +++ b/node-registrar/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.21-alpine AS builder +FROM golang:1.23-alpine AS builder WORKDIR /app diff --git a/node-registrar/Makefile b/node-registrar/Makefile index 5ad2827..bc77962 100644 --- a/node-registrar/Makefile +++ b/node-registrar/Makefile @@ -1,7 +1,7 @@ run: go run cmds/main.go --postgres-host localhost --postgres-port 5432 --postgres-db postgres --postgres-user postgres --postgres-password password --domain localhost --server-port 8080 -postgres: +start-postgres: docker run --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=password -e POSTGRES_DB=postgres -p 5432:5432 -d postgres stop-postgres: diff --git a/node-registrar/README.md b/node-registrar/README.md index 3820a90..12be537 100644 --- a/node-registrar/README.md +++ b/node-registrar/README.md @@ -1,10 +1,13 @@ # Node Registrar Service +[![Go Report Card](https://goreportcard.com/badge/github.com/threefoldtech/tfgrid-sdk-go/node-registrar)](https://goreportcard.com/report/github.com/threefoldtech/tfgrid-sdk-go/node-registrar) +[![GoDoc](https://godoc.org/github.com/threefoldtech/tfgrid-sdk-go/node-registrar?status.svg)](https://godoc.org/github.com/threefoldtech/tfgrid-sdk-go/node-registrar) + ## Overview -This project provides an API for registring zos nodes using the Go Gin framework and PostgreSQL database. -The API supports operations like registring, listing, and updating farms and nodes, as well as reporting uptime and consumption data for nodes. +This project provides an API for registring and managing zos nodes on ThreeFold GridV4. Built with the Go Gin framework and PostgreSQL database, +It offers operations like registring, listing, and updating farms and nodes, as well as reporting uptime and consumption data for nodes. ## Features @@ -31,25 +34,29 @@ The API supports operations like registring, listing, and updating farms and nod ### Farms Endpoints -1. **GET /farms/** - List all farms, or use FarmFilter to list specific set of farms. -2. **GET /farms/:farm_id** - Get a specific farm by ID. -3. **POST /farms/** - Create a new farm. -4. **PATCH /farms/** - Update an existing farm. +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/farms/` | List all farms with optional filtering | +| GET | `/farms/:farm_id` | Get a specific farm by ID | +| POST | `/farms/` | Create a new farm | +| PATCH | `/farms/` | Update an existing farm | ### Nodes Endpoints -1. **GET /nodes/** - List all nodes, or use NodeFilter to list specific set of nodes. -2. **GET /nodes/:node_id** - Get a specific node by ID. -3. **POST /nodes/** - Register a new node. -4. **POST /nodes/:node_id/uptime** - Report uptime for a specific node. -5. **POST /nodes/:node_id/consumption** - Report consumption for a specific node. +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/nodes/` | List all nodes with optional filtering | +| GET | `/nodes/:node_id` | Get a specific node by ID | +| POST | `/nodes/` | Register a new node | +| POST | `/nodes/:node_id/uptime` | Report uptime for a specific node | +| POST | `/nodes/:node_id/consumption` | Report consumption for a specific node | ## Setup Instructions 1. **Start PostgreSQL:** ```bash - make postgres + make start-postgres ``` 2. **Run the Server:** @@ -81,11 +88,11 @@ Replace `` and `` with the appropriate values. ## How to run the server with docker -1. use the docker file to build the docker image +1. To use the docker file to build the docker image, run this command in the root directory of the sdk - ```bash - docker build -t registrar:latest . - ``` +```bash +docker build -t registrar:latest -f node-registrar/Dockerfile . +``` 2. run the image @@ -125,14 +132,14 @@ X-Auth: Base64(Challenge):Base64(Signature) **Signature:** ED25519/SR25519 signature of challenge bytes -## Database Schema - -Key Tables: +The service uses a PostgreSQL database with the following key tables: -- `accounts` - Authentication credentials and relay configs -- `farms` - Farm metadata with owner relationship -- `nodes` - Node hardware/resources specification -- `uptime_reports` - Historical node availability data +| Table | Description | +|-------|-------------| +| `accounts` | Authentication credentials and relay configurations | +| `farms` | Farm metadata with owner relationship | +| `nodes` | Node hardware specifications and resources | +| `uptime_reports` | Historical node availability data | ## Development @@ -141,3 +148,41 @@ Key Tables: ```bash swag init -g pkg/server/handlers.go --output docs --parseDependency --parseDepth 2 ``` + +## Client Library + +A Go client library is available to interact with the Node Registrar Service. See the [client documentation](./client/README.md) for details on installation and usage. + +Example usage: + +```go +import "github.com/threefoldtech/tfgrid-sdk-go/node-registrar/client" + +// Initialize client +cli, err := client.NewRegistrarClient("https://registrar.dev.grid.tf", mnemonic) + +// Register a node + node := Node{ + TwinID: twinID, + FarmID: farmID, + Interfaces: interfaces, + Location: location, + Resources: resources, + SerialNumber: serialNumber, + SecureBoot: secureBoot, + Virtualized: virtualized, + } +nodeID, err := cli.RegisterNode(node) +``` + +### Generating Swagger Documentation + +```bash +swag init -g pkg/server/handlers.go --output docs --parseDependency --parseDepth 2 +``` + +### Running Tests + +```bash +make test +``` diff --git a/node-registrar/client/account.go b/node-registrar/client/account.go index f5e3c5b..32a9ec5 100644 --- a/node-registrar/client/account.go +++ b/node-registrar/client/account.go @@ -10,7 +10,7 @@ import ( "time" "github.com/pkg/errors" - "github.com/vedhavyas/go-subkey/v2" + subkey "github.com/vedhavyas/go-subkey/v2" ) var ErrorAccountNotFound = fmt.Errorf("failed to get requested account from node registrar") diff --git a/node-registrar/client/client.go b/node-registrar/client/client.go index eb29e16..32ef377 100644 --- a/node-registrar/client/client.go +++ b/node-registrar/client/client.go @@ -4,7 +4,7 @@ import ( "net/http" "github.com/pkg/errors" - "github.com/vedhavyas/go-subkey/v2" + subkey "github.com/vedhavyas/go-subkey/v2" ) type RegistrarClient struct { diff --git a/node-registrar/client/farm.go b/node-registrar/client/farm.go index f4112e0..88fc1a8 100644 --- a/node-registrar/client/farm.go +++ b/node-registrar/client/farm.go @@ -20,9 +20,9 @@ func (c *RegistrarClient) CreateFarm(farmName, stellarAddr string, dedicated boo return c.createFarm(farmName, stellarAddr, dedicated) } -// UpdateFarm update farm configuration (farmName, stellarAddress, dedicated). -func (c *RegistrarClient) UpdateFarm(farmID uint64, opts ...UpdateFarmOpts) (err error) { - return c.updateFarm(farmID, opts) +// UpdateFarm updates an existing farm's configuration +func (c *RegistrarClient) UpdateFarm(farmID uint64, update FarmUpdate) (err error) { + return c.updateFarm(farmID, update) } // GetFarm get a farm using its farmID @@ -30,87 +30,26 @@ func (c *RegistrarClient) GetFarm(farmID uint64) (farm Farm, err error) { return c.getFarm(farmID) } -// ListFarms get a list of farm using ListFarmOpts -func (c *RegistrarClient) ListFarms(opts ...ListFarmOpts) (farms []Farm, err error) { - return c.listFarms(opts...) +// ListFarms gets a list of farms using filter options +func (c *RegistrarClient) ListFarms(filter FarmFilter) (farms []Farm, err error) { + return c.listFarms(filter) } -type farmCfg struct { - farmName string - farmID uint64 - twinID uint64 - dedicated bool - stellarAddress string - page uint32 - size uint32 +// FarmUpdate represents the data needed to update an existing farm +type FarmUpdate struct { + FarmName *string + StellarAddress *string + Dedicated *bool } -type ( - ListFarmOpts func(*farmCfg) - UpdateFarmOpts func(*farmCfg) -) - -// ListFarmWithName lists farms with farm name -func ListFarmWithName(name string) ListFarmOpts { - return func(n *farmCfg) { - n.farmName = name - } -} - -// ListFarmWithFarmID lists farms with farmID -func ListFarmWithFarmID(id uint64) ListFarmOpts { - return func(n *farmCfg) { - n.farmID = id - } -} - -// ListFarmWithTwinID lists farms with twinID -func ListFarmWithTwinID(id uint64) ListFarmOpts { - return func(n *farmCfg) { - n.twinID = id - } -} - -// ListFarmWithDedicated lists dedicated farms -func ListFarmWithDedicated() ListFarmOpts { - return func(n *farmCfg) { - n.dedicated = true - } -} - -// ListFarmWithPage lists farms in a certain page -func ListFarmWithPage(page uint32) ListFarmOpts { - return func(n *farmCfg) { - n.page = page - } -} - -// ListFarmWithPage lists size number of farms -func ListFarmWithSize(size uint32) ListFarmOpts { - return func(n *farmCfg) { - n.size = size - } -} - -// UpdateFarmWithName update farm name -func UpdateFarmWithName(name string) UpdateFarmOpts { - return func(n *farmCfg) { - n.farmName = name - } -} - -// UpdateFarmWithName set farm status to dedicated -func UpdateFarmWithDedicated() UpdateFarmOpts { - return func(n *farmCfg) { - n.dedicated = true - } -} - -// UpdateFarmWithName set farm status to dedicated -func UpdateFarmWithStellarAddress(address string) UpdateFarmOpts { - return func(n *farmCfg) { - n.stellarAddress = address - } +// FarmFilter represents filtering options for listing farms +type FarmFilter struct { + FarmID *uint64 + FarmName *string + TwinID *uint64 + Dedicated *bool + Page *uint32 + Size *uint32 } func (c *RegistrarClient) createFarm(farmName, stellarAddr string, dedicated bool) (farmID uint64, err error) { @@ -177,7 +116,7 @@ func (c *RegistrarClient) createFarm(farmName, stellarAddr string, dedicated boo return result.FarmID, nil } -func (c *RegistrarClient) updateFarm(farmID uint64, opts []UpdateFarmOpts) (err error) { +func (c *RegistrarClient) updateFarm(farmID uint64, update FarmUpdate) (err error) { if err = c.ensureTwinID(); err != nil { return errors.Wrap(err, "failed to ensure twin id") } @@ -188,7 +127,7 @@ func (c *RegistrarClient) updateFarm(farmID uint64, opts []UpdateFarmOpts) (err } var body bytes.Buffer - data := parseUpdateFarmOpts(opts) + data := parseUpdateFarmOpts(update) if stellarAddr, ok := data["stellar_address"]; ok { if err = validateStellarAddress(stellarAddr.(string)); err != nil { @@ -224,7 +163,7 @@ func (c *RegistrarClient) updateFarm(farmID uint64, opts []UpdateFarmOpts) (err if resp.StatusCode != http.StatusOK { err = parseResponseError(resp.Body) - return errors.Wrapf(err, "failed to create farm with status code %s", resp.Status) + return errors.Wrapf(err, "failed to update farm with status code %s", resp.Status) } return @@ -261,13 +200,13 @@ func (c *RegistrarClient) getFarm(id uint64) (farm Farm, err error) { return } -func (c *RegistrarClient) listFarms(opts ...ListFarmOpts) (farms []Farm, err error) { +func (c *RegistrarClient) listFarms(filter FarmFilter) (farms []Farm, err error) { url, err := url.JoinPath(c.baseURL, "farms") if err != nil { return farms, errors.Wrap(err, "failed to construct registrar url") } - data := parseListFarmOpts(opts) + data := parseListFarmOpts(filter) req, err := http.NewRequest("GET", url, nil) if err != nil { @@ -303,63 +242,53 @@ func (c *RegistrarClient) listFarms(opts ...ListFarmOpts) (farms []Farm, err err return } -func parseListFarmOpts(opts []ListFarmOpts) map[string]any { - cfg := farmCfg{ - farmName: "", - farmID: 0, - twinID: 0, - dedicated: false, - page: 1, - size: 50, - } +func parseListFarmOpts(filter FarmFilter) map[string]any { + data := map[string]any{} - for _, opt := range opts { - opt(&cfg) + if filter.FarmName != nil && *filter.FarmName != "" { + data["farm_name"] = *filter.FarmName } - data := map[string]any{} - - if len(cfg.farmName) != 0 { - data["farm_name"] = cfg.farmName + if filter.FarmID != nil { + data["farm_id"] = *filter.FarmID } - if cfg.farmID != 0 { - data["farm_id"] = cfg.farmID + if filter.TwinID != nil { + data["twin_id"] = *filter.TwinID } - if cfg.twinID != 0 { - data["twin_id"] = cfg.twinID + if filter.Dedicated != nil { + data["dedicated"] = *filter.Dedicated } - if cfg.dedicated { - data["dedicated"] = true + page := uint32(1) + if filter.Page != nil { + page = *filter.Page } + data["page"] = page - data["page"] = cfg.page - data["size"] = cfg.size + size := uint32(50) + if filter.Size != nil { + size = *filter.Size + } + data["size"] = size return data } -func parseUpdateFarmOpts(opts []UpdateFarmOpts) map[string]any { - cfg := farmCfg{} - - for _, opt := range opts { - opt(&cfg) - } - +func parseUpdateFarmOpts(update FarmUpdate) map[string]any { data := map[string]any{} - if len(cfg.farmName) != 0 { - data["farm_name"] = cfg.farmName + if update.FarmName != nil { + data["farm_name"] = *update.FarmName } - if cfg.dedicated { - data["dedicated"] = true + if update.StellarAddress != nil { + data["stellar_address"] = *update.StellarAddress } - if len(cfg.stellarAddress) != 0 { - data["stellar_address"] = cfg.stellarAddress + if update.Dedicated != nil { + data["dedicated"] = *update.Dedicated } return data diff --git a/node-registrar/client/farm_test.go b/node-registrar/client/farm_test.go index c75efac..d02baf6 100644 --- a/node-registrar/client/farm_test.go +++ b/node-registrar/client/farm_test.go @@ -77,7 +77,8 @@ func TestUpdateFarm(t *testing.T) { require.NoError(err) request = updateFarmWithStatusUnauthorized - err = c.UpdateFarm(farmID, UpdateFarmWithName("notFreeFarm")) + name := "notFreeFarm" + err = c.UpdateFarm(farmID, FarmUpdate{FarmName: &name}) require.Error(err) }) @@ -88,7 +89,8 @@ func TestUpdateFarm(t *testing.T) { require.NoError(err) request = updateFarmWithStatusOK - err = c.UpdateFarm(farmID, UpdateFarmWithName("notFreeFarm")) + name := "notFreeFarm" + err = c.UpdateFarm(farmID, FarmUpdate{FarmName: &name}) require.NoError(err) }) } diff --git a/node-registrar/client/mnemonic.go b/node-registrar/client/mnemonic.go index ea0ef5b..7865261 100644 --- a/node-registrar/client/mnemonic.go +++ b/node-registrar/client/mnemonic.go @@ -1,11 +1,11 @@ package client import ( - "github.com/cosmos/go-bip39" + bip39 "github.com/cosmos/go-bip39" "github.com/pkg/errors" subkeyEd25519 "github.com/vedhavyas/go-subkey/v2/ed25519" - "github.com/vedhavyas/go-subkey/v2" + subkey "github.com/vedhavyas/go-subkey/v2" ) func (c *RegistrarClient) Mnemonic() string { diff --git a/node-registrar/client/node.go b/node-registrar/client/node.go index 8a67dbc..5d5eade 100644 --- a/node-registrar/client/node.go +++ b/node-registrar/client/node.go @@ -7,7 +7,6 @@ import ( "io" "net/http" "net/url" - "reflect" "strings" "time" @@ -22,8 +21,8 @@ func (c *RegistrarClient) RegisterNode(node Node) (nodeID uint64, err error) { } // UpdateNode update node configuration (farmID, interfaces, resources, location, secureBoot, virtualized). -func (c *RegistrarClient) UpdateNode(opts ...UpdateNodeOpts) (err error) { - return c.updateNode(opts) +func (c *RegistrarClient) UpdateNode(updateOpts NodeUpdate) (err error) { + return c.updateNode(updateOpts) } // ReportUptime update node Uptime. @@ -42,141 +41,8 @@ func (c *RegistrarClient) GetNodeByTwinID(id uint64) (node Node, err error) { } // ListNodes lists registered nodes details using (nodeID, twinID, farmID). -func (c *RegistrarClient) ListNodes(opts ...ListNodeOpts) (nodes []Node, err error) { - return c.listNodes(opts) -} - -type nodeCfg struct { - nodeID uint64 - farmID uint64 - twinID uint64 - status string - healthy bool - online *bool - lastSeen *int64 - Location Location - Resources Resources - Interfaces []Interface - SecureBoot bool - Virtualized bool - SerialNumber string - UptimeReports []UptimeReport - Approved bool - page uint32 - size uint32 -} - -type ( - ListNodeOpts func(*nodeCfg) - UpdateNodeOpts func(*nodeCfg) -) - -func ListNodesWithNodeID(id uint64) ListNodeOpts { - return func(n *nodeCfg) { - n.nodeID = id - } -} - -func ListNodesWithFarmID(id uint64) ListNodeOpts { - return func(n *nodeCfg) { - n.farmID = id - } -} - -func ListNodesWithStatus(status string) ListNodeOpts { - return func(n *nodeCfg) { - n.status = status - } -} - -func ListNodesWithHealthy() ListNodeOpts { - return func(n *nodeCfg) { - n.healthy = true - } -} - -func ListNodesWithOnline(online bool) ListNodeOpts { - return func(n *nodeCfg) { - n.online = &online - } -} - -func ListNodesWithLastSeen(minutes int64) ListNodeOpts { - return func(n *nodeCfg) { - n.lastSeen = &minutes - } -} - -func ListNodesWithTwinID(id uint64) ListNodeOpts { - return func(n *nodeCfg) { - n.twinID = id - } -} - -func ListNodesWithPage(page uint32) ListNodeOpts { - return func(n *nodeCfg) { - n.page = page - } -} - -func ListNodesWithSize(size uint32) ListNodeOpts { - return func(n *nodeCfg) { - n.size = size - } -} - -func UpdateNodesWithFarmID(id uint64) UpdateNodeOpts { - return func(n *nodeCfg) { - n.farmID = id - } -} - -func UpdateNodesWithInterfaces(interfaces []Interface) UpdateNodeOpts { - return func(n *nodeCfg) { - n.Interfaces = interfaces - } -} - -func UpdateNodesWithLocation(location Location) UpdateNodeOpts { - return func(n *nodeCfg) { - n.Location = location - } -} - -func UpdateNodesWithResources(resources Resources) UpdateNodeOpts { - return func(n *nodeCfg) { - n.Resources = resources - } -} - -func UpdateNodesWithSerialNumber(serialNumbe string) UpdateNodeOpts { - return func(n *nodeCfg) { - n.SerialNumber = serialNumbe - } -} - -func UpdateNodesWithSecureBoot() UpdateNodeOpts { - return func(n *nodeCfg) { - n.SecureBoot = true - } -} - -func UpdateNodesWithVirtualized() UpdateNodeOpts { - return func(n *nodeCfg) { - n.Virtualized = true - } -} - -func UpdateNodeWithStatus(status string) UpdateNodeOpts { - return func(n *nodeCfg) { - n.status = status - } -} - -func UpdateNodeWithHealthy() UpdateNodeOpts { - return func(n *nodeCfg) { - n.healthy = true - } +func (c *RegistrarClient) ListNodes(opts NodeFilter) (nodes []Node, err error) { + return c.listNodesWithFilter(opts) } func (c *RegistrarClient) registerNode(node Node) (nodeID uint64, err error) { @@ -253,7 +119,21 @@ func (c *RegistrarClient) registerNode(node Node) (nodeID uint64, err error) { return nodeID, nil } -func (c *RegistrarClient) updateNode(opts []UpdateNodeOpts) (err error) { +// NodeUpdate represents update options for a node +type NodeUpdate struct { + FarmID *uint64 + Location *Location + Resources *Resources + Interfaces []Interface + SecureBoot *bool + Virtualized *bool + SerialNumber *string + Status *string + Healthy *bool + Approved *bool +} + +func (c *RegistrarClient) updateNode(opts NodeUpdate) (err error) { err = c.ensureNodeID() if err != nil { return err @@ -428,7 +308,7 @@ func (c *RegistrarClient) getNode(id uint64) (node Node, err error) { } func (c *RegistrarClient) getNodeByTwinID(id uint64) (node Node, err error) { - nodes, err := c.ListNodes(ListNodesWithTwinID(id)) + nodes, err := c.ListNodes(NodeFilter{TwinID: &id}) if err != nil { return } @@ -440,7 +320,20 @@ func (c *RegistrarClient) getNodeByTwinID(id uint64) (node Node, err error) { return nodes[0], nil } -func (c *RegistrarClient) listNodes(opts []ListNodeOpts) (nodes []Node, err error) { +// NodeFilter represents filtering options for listing nodes +type NodeFilter struct { + NodeID *uint64 + FarmID *uint64 + TwinID *uint64 + Status *string + Healthy *bool + Online *bool + LastSeen *int64 + Page *uint32 + Size *uint32 +} + +func (c *RegistrarClient) listNodesWithFilter(filter NodeFilter) (nodes []Node, err error) { url, err := url.JoinPath(c.baseURL, "nodes") if err != nil { return nodes, errors.Wrap(err, "failed to construct registrar url") @@ -452,7 +345,7 @@ func (c *RegistrarClient) listNodes(opts []ListNodeOpts) (nodes []Node, err erro } q := req.URL.Query() - data := parseListNodeOpts(opts) + data := parseListNodeOpts(filter) for key, val := range data { q.Add(key, fmt.Sprint(val)) @@ -532,89 +425,78 @@ func (c *RegistrarClient) ensureNodeID() error { return nil } -func (c *RegistrarClient) parseUpdateNodeOpts(node Node, opts []UpdateNodeOpts) Node { - cfg := nodeCfg{ - farmID: 0, - Location: Location{}, - Resources: Resources{}, - Interfaces: []Interface{}, - SecureBoot: false, - Virtualized: false, - Approved: false, +func (c *RegistrarClient) parseUpdateNodeOpts(node Node, update NodeUpdate) Node { + if update.FarmID != nil { + node.FarmID = *update.FarmID } - - for _, opt := range opts { - opt(&cfg) + if update.Location != nil { + node.Location = *update.Location } - - if cfg.farmID != 0 { - node.FarmID = cfg.farmID + if update.Resources != nil { + node.Resources = *update.Resources } - - if !reflect.DeepEqual(cfg.Location, Location{}) { - node.Location = cfg.Location + if len(update.Interfaces) > 0 { + node.Interfaces = update.Interfaces } - - if !reflect.DeepEqual(cfg.Resources, Resources{}) { - node.Resources = cfg.Resources + if update.SerialNumber != nil { + node.SerialNumber = *update.SerialNumber } - - if len(cfg.Interfaces) != 0 { - node.Interfaces = cfg.Interfaces + if update.SecureBoot != nil { + node.SecureBoot = *update.SecureBoot + } + if update.Virtualized != nil { + node.Virtualized = *update.Virtualized + } + if update.Status != nil { + node.Virtualized = *update.Virtualized + } + if update.Healthy != nil { + node.Virtualized = *update.Virtualized + } + if update.Approved != nil { + node.Virtualized = *update.Virtualized } return node } -func parseListNodeOpts(opts []ListNodeOpts) map[string]any { - cfg := nodeCfg{ - nodeID: 0, - twinID: 0, - farmID: 0, - status: "", - healthy: false, - online: nil, - lastSeen: nil, - size: 50, - page: 1, - } - - for _, opt := range opts { - opt(&cfg) - } - +func parseListNodeOpts(filter NodeFilter) map[string]any { data := map[string]any{} - if cfg.nodeID != 0 { - data["node_id"] = cfg.nodeID + if filter.NodeID != nil { + data["node_id"] = *filter.NodeID } - - if cfg.twinID != 0 { - data["twin_id"] = cfg.twinID + if filter.TwinID != nil { + data["twin_id"] = *filter.TwinID } - - if cfg.farmID != 0 { - data["farm_id"] = cfg.farmID + if filter.FarmID != nil { + data["farm_id"] = *filter.FarmID } - - if len(cfg.status) != 0 { - data["status"] = cfg.status + if filter.Status != nil && *filter.Status != "" { + data["status"] = *filter.Status } - - if cfg.healthy { - data["healthy"] = cfg.healthy + if filter.Healthy != nil { + data["healthy"] = *filter.Healthy + } + if filter.Online != nil { + data["online"] = *filter.Online + } + if filter.LastSeen != nil { + data["last_seen"] = *filter.LastSeen } - if cfg.online != nil { - data["online"] = *cfg.online + page := uint32(1) + if filter.Page != nil { + page = *filter.Page } + data["page"] = page - if cfg.lastSeen != nil { - data["last_seen"] = *cfg.lastSeen + size := uint32(50) + if filter.Size != nil { + size = *filter.Size } - data["size"] = cfg.size - data["page"] = cfg.page + data["size"] = size return data } diff --git a/node-registrar/client/node_test.go b/node-registrar/client/node_test.go index bbac795..b59d356 100644 --- a/node-registrar/client/node_test.go +++ b/node-registrar/client/node_test.go @@ -96,7 +96,8 @@ func TestUpdateNode(t *testing.T) { t.Run("test update node with status ok", func(t *testing.T) { count = 0 request = updateNodeStatusOK - err = c.UpdateNode(UpdateNodesWithFarmID(2)) + var farmID uint64 = 2 + err = c.UpdateNode(NodeUpdate{FarmID: &farmID}) require.NoError(err) }) @@ -159,7 +160,8 @@ func TestGetNode(t *testing.T) { t.Run("test list nodes of specific farm", func(t *testing.T) { request = listNodesInFarm - result, err := c.ListNodes(ListNodesWithFarmID(farmID)) + id := farmID + result, err := c.ListNodes(NodeFilter{FarmID: &id}) require.NoError(err) require.Equal([]Node{node}, result) }) diff --git a/node-registrar/go.mod b/node-registrar/go.mod index 2e0b1d2..693faef 100644 --- a/node-registrar/go.mod +++ b/node-registrar/go.mod @@ -1,6 +1,6 @@ module github.com/threefoldtech/tfgrid4-sdk-go/node-registrar -go 1.21.0 +go 1.23.0 require ( github.com/cosmos/go-bip39 v1.0.0 diff --git a/node-registrar/pkg/server/sigverifier.go b/node-registrar/pkg/server/sigverifier.go index 8b80c99..b92067c 100644 --- a/node-registrar/pkg/server/sigverifier.go +++ b/node-registrar/pkg/server/sigverifier.go @@ -2,7 +2,7 @@ package server import ( "github.com/pkg/errors" - "github.com/vedhavyas/go-subkey/v2" + subkey "github.com/vedhavyas/go-subkey/v2" "github.com/vedhavyas/go-subkey/v2/ed25519" "github.com/vedhavyas/go-subkey/v2/sr25519" ) diff --git a/rmb-sdk-go/go.mod b/rmb-sdk-go/go.mod index 2a4c96c..cf66893 100644 --- a/rmb-sdk-go/go.mod +++ b/rmb-sdk-go/go.mod @@ -1,6 +1,6 @@ module github.com/threefoldtech/tfgrid-sdk-go/rmb-sdk-go -go 1.21 +go 1.23 require ( github.com/ChainSafe/go-schnorrkel v1.1.0