Skip to content
This repository has been archived by the owner on Jul 18, 2024. It is now read-only.

Commit

Permalink
Forward Inventory to FleetDB (#19)
Browse files Browse the repository at this point in the history
* forward inventory to fleetdb

* cleanup code
  • Loading branch information
Alva8756 authored Apr 25, 2024
1 parent 1acc2d2 commit c7dd9b1
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 143 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21.3-alpine3.18 AS build
FROM golang:1.22.2-alpine3.18 AS build

ARG APP_NAME
WORKDIR "/go/src/github.com/metal-toolbox/${APP_NAME}"
Expand Down
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
module github.com/metal-toolbox/component-inventory

go 1.21
go 1.22

toolchain go1.22.2

require (
github.com/bmc-toolbox/common v0.0.0-20240416132216-a56a09c16f4e
github.com/equinix-labs/otel-init-go v0.0.9
github.com/gin-gonic/gin v1.9.1
github.com/google/uuid v1.6.0
github.com/metal-toolbox/alloy v0.3.3-0.20240320183632-05dfbd5e9110
github.com/metal-toolbox/fleetdb v0.17.1
github.com/metal-toolbox/alloy v0.3.3-0.20240415055734-d09250fed38a
github.com/metal-toolbox/fleetdb v0.17.2-0.20240419204835-60c421433f0a
github.com/metal-toolbox/rivets v1.0.3
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.19.0
Expand Down
11 changes: 5 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -500,14 +500,13 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/metal-toolbox/alloy v0.3.3-0.20240320183632-05dfbd5e9110 h1:v3PTHUK9KIF/mEmctQ5mgsBtG8dtdfboHI/7QQg/Ft0=
github.com/metal-toolbox/alloy v0.3.3-0.20240320183632-05dfbd5e9110/go.mod h1:NcVZtbVhBCcpyTHew0TJ8yzon69vMf+rGMLoGb9jTa0=
github.com/metal-toolbox/fleetdb v0.17.1 h1:eyaCg4yGQnbXIjiBMZPsLqmEpxbltlEUNDG+Mn27Fsc=
github.com/metal-toolbox/fleetdb v0.17.1/go.mod h1:TbRbU+ppHIknqiAONR7JDQfzKij30uiPkehgxlA1Hv0=
github.com/metal-toolbox/alloy v0.3.3-0.20240415055734-d09250fed38a h1:w6uita6zAXZoGn75vcnEDW2Y6K1rYeLWnv7EXAemByE=
github.com/metal-toolbox/alloy v0.3.3-0.20240415055734-d09250fed38a/go.mod h1:Sv+oD+xnACNV3E7r9kmtgUi1LGKQumTv7mpWOe8a2VU=
github.com/metal-toolbox/fleetdb v0.17.2-0.20240419204835-60c421433f0a h1:Jrjbk0M8EiljlgxeLso232AizQzuaQ4Bnqf8L9ITkug=
github.com/metal-toolbox/fleetdb v0.17.2-0.20240419204835-60c421433f0a/go.mod h1:U9YArAdibILSBJzNbmtJAz6m+Cljce6a7pL0JkOckqQ=
github.com/metal-toolbox/rivets v1.0.3 h1:ZW9q8V3vz6VxAczC4eR1YJdl+kapHF3ebVc+4r3NmR8=
github.com/metal-toolbox/rivets v1.0.3/go.mod h1:EMQJRT1mjIyFRXxvKNaBlz7Z4Sp88rTaGO8W18olN2I=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
Expand Down
52 changes: 0 additions & 52 deletions internal/fleetdb/attributes.go

This file was deleted.

91 changes: 25 additions & 66 deletions internal/fleetdb/fleetdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,24 @@ package internalfleetdb

import (
"context"
"encoding/json"
"fmt"

"github.com/google/uuid"
"github.com/metal-toolbox/alloy/types"
"github.com/metal-toolbox/component-inventory/internal/app"
"github.com/metal-toolbox/component-inventory/pkg/api/constants"
"github.com/metal-toolbox/component-inventory/internal/inventoryconverter"
fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1"
"go.uber.org/zap"
)

type Client interface {
GetServer(context.Context, uuid.UUID) (*fleetdb.Server, *fleetdb.ServerResponse, error)
GetComponents(context.Context, uuid.UUID, *fleetdb.PaginationParams) (fleetdb.ServerComponentSlice, *fleetdb.ServerResponse, error)
UpdateAttributes(context.Context, *fleetdb.Server, *types.InventoryDevice, *zap.Logger) error
UpdateServerBIOSConfig() error
UpdateServerInventory(context.Context, *fleetdb.Server, *types.InventoryDevice, *zap.Logger, bool) error
}

// Creates a new Client, with reasonable defaults
func NewFleetDBClient(cfg *app.Configuration) (Client, error) {
func NewFleetDBClient(ctx context.Context, cfg *app.Configuration) (Client, error) {
client, err := fleetdb.NewClient(cfg.FleetDBAddress, nil)
if err != nil {
return nil, err
Expand All @@ -31,13 +29,26 @@ func NewFleetDBClient(cfg *app.Configuration) (Client, error) {
client.SetToken(cfg.FleetDBToken)
}

slugs := make(map[string]*fleetdb.ServerComponentType)
serverComponentTypes, _, err := client.ListServerComponentTypes(ctx, nil)
if err != nil {
return nil, fmt.Errorf("failed to get server component types: %w", err)
}
for _, ct := range serverComponentTypes {
slugs[ct.Slug] = ct
}

return &fleetDBClient{
client: client,
client: client,
slugs: slugs,
inventoryConverterInstance: inventoryconverter.NewInventoryConverter(slugs),
}, nil
}

type fleetDBClient struct {
client *fleetdb.Client
client *fleetdb.Client
slugs map[string]*fleetdb.ServerComponentType
inventoryConverterInstance *inventoryconverter.InventoryConverter
}

func (fc fleetDBClient) GetServer(ctx context.Context, id uuid.UUID) (*fleetdb.Server, *fleetdb.ServerResponse, error) {
Expand All @@ -48,69 +59,17 @@ func (fc fleetDBClient) GetComponents(ctx context.Context, id uuid.UUID, params
return fc.client.GetComponents(ctx, id, params)
}

func (fc fleetDBClient) UpdateAttributes(ctx context.Context, server *fleetdb.Server, dev *types.InventoryDevice, log *zap.Logger) error {
return createUpdateServerAttributes(ctx, fc.client, server, dev, log)
}

// Functions below may be refactored in the near future.
func createUpdateServerAttributes(ctx context.Context, c *fleetdb.Client, server *fleetdb.Server, dev *types.InventoryDevice, log *zap.Logger) error {
newVendorData, newVendorAttrs, err := deviceVendorAttributes(dev)
func (fc fleetDBClient) UpdateServerInventory(ctx context.Context, server *fleetdb.Server, dev *types.InventoryDevice, log *zap.Logger, inband bool) error {
log.Info("update server inventory", zap.String("server", server.Name))
rivetsServer, err := fc.inventoryConverterInstance.ToRivetsServer(server.UUID.String(), server.FacilityCode, dev.Inv, dev.BiosCfg)
if err != nil {
log.Error("convert inventory fail", zap.String("server", server.Name), zap.String("err", err.Error()))
return err
}

// identify current vendor data in the inventory
existingVendorAttrs := attributeByNamespace(constants.ServerVendorAttributeNS, server.Attributes)
if existingVendorAttrs == nil {
// create if none exists
_, err = c.CreateAttributes(ctx, server.UUID, *newVendorAttrs)
return err
}

// unpack vendor data from inventory
existingVendorData := map[string]string{}
if err := json.Unmarshal(existingVendorAttrs.Data, &existingVendorData); err != nil {
// update vendor data since it seems to be invalid
log.Warn("server vendor attributes data invalid, updating..")

_, err = c.UpdateAttributes(ctx, server.UUID, constants.ServerVendorAttributeNS, newVendorAttrs.Data)

return err
}

updatedVendorData := existingVendorData
var changes bool
for key := range newVendorData {
if updatedVendorData[key] == "" || updatedVendorData[key] == "unknown" {
if newVendorData[key] != "unknown" {
changes = true
updatedVendorData[key] = newVendorData[key]
}
}
}

if !changes {
return nil
}

if len(updatedVendorData) > 0 {
updateBytes, err := json.Marshal(updatedVendorData)
if err != nil {
return err
}

_, err = c.UpdateAttributes(ctx, server.UUID, constants.ServerVendorAttributeNS, updateBytes)

_, err = fc.client.SetServerInventory(ctx, server.UUID, rivetsServer, inband)
if err != nil {
log.Error("set inventory fail", zap.String("server", server.Name), zap.String("err", err.Error()))
return err
}

return nil
}

func (fc fleetDBClient) UpdateServerBIOSConfig() error {
return createUpdateServerBIOSConfig()
}

func createUpdateServerBIOSConfig() error {
return fmt.Errorf("unimplemented")
}
60 changes: 51 additions & 9 deletions pkg/api/routes/components_test.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
package routes

import (
"github.com/metal-toolbox/component-inventory/internal/app"
internalfleetdb "github.com/metal-toolbox/component-inventory/internal/fleetdb"

"context"
"encoding/json"
"fmt"

"github.com/google/uuid"
fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1"

"net/http"
"net/http/httptest"
"testing"

"github.com/google/uuid"
"github.com/metal-toolbox/component-inventory/internal/app"
"github.com/stretchr/testify/require"

internalfleetdb "github.com/metal-toolbox/component-inventory/internal/fleetdb"
fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1"
)

var serverUUID = uuid.New()
Expand Down Expand Up @@ -43,6 +42,19 @@ var validComponents = fleetdb.ServerComponentSlice{
},
}

var validComponentTypes = fleetdb.ServerComponentTypeSlice{
&fleetdb.ServerComponentType{
ID: "02dc2503-b64c-439b-9f25-8e130705f14a",
Name: "Backplane-Expander",
Slug: "backplane-expander",
},
&fleetdb.ServerComponentType{
ID: "1e0c3417-d63c-4fd5-88f7-4c525c70da12",
Name: "Mainboard",
Slug: "mainboard",
},
}

func getComponentsHandler(t *testing.T, comps *fleetdb.ServerComponentSlice, code int) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
var byt []byte
Expand All @@ -67,6 +79,30 @@ func getComponentsHandler(t *testing.T, comps *fleetdb.ServerComponentSlice, cod
}
}

func getComponentTypesHandler(t *testing.T, comps *fleetdb.ServerComponentSlice, code int) http.HandlerFunc {
return func(w http.ResponseWriter, _ *http.Request) {
var byt []byte
if comps != nil {
var err error
srvResponse := fleetdb.ServerResponse{
Records: validComponentTypes,
}
byt, err = json.Marshal(srvResponse)
if err != nil {
t.Fatalf("serializing server response: %s", err.Error())
}
}

w.WriteHeader(code)
if byt != nil {
_, err := w.Write(byt)
if err != nil {
t.Fatalf("writing http response: %s", err.Error())
}
}
}
}

func TestFetchServerComponents(t *testing.T) {
t.Parallel()
t.Run("valid component return", func(t *testing.T) {
Expand All @@ -82,6 +118,9 @@ func TestFetchServerComponents(t *testing.T) {
w.WriteHeader(http.StatusBadRequest)
},
)
mux.HandleFunc("/api/v1/server-component-types",
getComponentTypesHandler(t, &validComponents, 200),
)
mux.HandleFunc(
fmt.Sprintf("/api/v1/servers/%s/components", serverUUID),
getComponentsHandler(t, &validComponents, 200),
Expand All @@ -91,7 +130,7 @@ func TestFetchServerComponents(t *testing.T) {

logger := app.GetLogger(true)

client, err := internalfleetdb.NewFleetDBClient(&app.Configuration{
client, err := internalfleetdb.NewFleetDBClient(context.Background(), &app.Configuration{
FleetDBAddress: ts.URL,
})
require.NoError(t, err)
Expand All @@ -111,6 +150,9 @@ func TestFetchServerComponents(t *testing.T) {
w.WriteHeader(http.StatusBadRequest)
},
)
mux.HandleFunc("/api/v1/server-component-types",
getComponentTypesHandler(t, &validComponents, 200),
)
mux.HandleFunc(
fmt.Sprintf("/api/v1/servers/%s/components", serverUUID),
getComponentsHandler(t, nil, 500),
Expand All @@ -120,7 +162,7 @@ func TestFetchServerComponents(t *testing.T) {

logger := app.GetLogger(true)

client, err := internalfleetdb.NewFleetDBClient(&app.Configuration{
client, err := internalfleetdb.NewFleetDBClient(context.Background(), &app.Configuration{
FleetDBAddress: ts.URL,
})
require.NoError(t, err)
Expand Down
29 changes: 25 additions & 4 deletions pkg/api/routes/inventory.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package routes

import (
"context"
"errors"
"fmt"
"strings"

"github.com/metal-toolbox/alloy/types"
internalfleetdb "github.com/metal-toolbox/component-inventory/internal/fleetdb"
Expand All @@ -12,13 +13,33 @@ import (

func processInband(ctx context.Context, c internalfleetdb.Client, server *fleetdb.Server, dev *types.InventoryDevice, log *zap.Logger) error { //nolint
log.Info("processing", zap.String("server", server.Name), zap.String("device", dev.Inv.Serial))
if err := c.UpdateAttributes(ctx, server, dev, log); err != nil {
if err := verifyComponent(c, server, dev, log); err != nil {
return err
}
return errors.New("not implemented")
return c.UpdateServerInventory(ctx, server, dev, log, true)
}

func processOutofband(ctx context.Context, c internalfleetdb.Client, server *fleetdb.Server, dev *types.InventoryDevice, log *zap.Logger) error { //nolint
log.Info("processing", zap.String("server", server.Name), zap.String("device", dev.Inv.Serial))
return errors.New("not implemented")
if err := verifyComponent(c, server, dev, log); err != nil {
log.Error("verify component", zap.String("server", server.Name), zap.String("err", err.Error()))
return err
}
return c.UpdateServerInventory(ctx, server, dev, log, false)
}

func verifyComponent(c internalfleetdb.Client, server *fleetdb.Server, dev *types.InventoryDevice, log *zap.Logger) error {
components, err := fetchServerComponents(c, server.UUID, log)
if err != nil {
if strings.Contains(err.Error(), "404") {
// The server doesn't have components, we can create it.
return nil
}
return err
}
return isBadComponents(components, dev)
}

func isBadComponents(_ serverComponents, _ *types.InventoryDevice) error {
return fmt.Errorf("unimplement")
}
Loading

0 comments on commit c7dd9b1

Please sign in to comment.