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

Outline of the internal fleetdb interaction #9

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions internal/fleetdb/attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package internalfleetdb

import (
"encoding/json"

"github.com/metal-toolbox/component-inventory/pkg/api/constants"
"github.com/metal-toolbox/component-inventory/pkg/api/types"
fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1"
)

func deviceVendorAttributes(cid *types.ComponentInventoryDevice) (map[string]string, *fleetdb.Attributes, error) {
deviceVendorData := map[string]string{
constants.ServerSerialAttributeKey: "unknown",
constants.ServerVendorAttributeKey: "unknown",
constants.ServerModelAttributeKey: "unknown",
}

if cid.Inv != nil {
if cid.Inv.Serial != "" {
deviceVendorData[constants.ServerSerialAttributeKey] = cid.Inv.Serial
}

if cid.Inv.Model != "" {
deviceVendorData[constants.ServerModelAttributeKey] = cid.Inv.Model
}

if cid.Inv.Vendor != "" {
deviceVendorData[constants.ServerVendorAttributeKey] = cid.Inv.Vendor
}
}

deviceVendorDataBytes, err := json.Marshal(deviceVendorData)
if err != nil {
return nil, nil, err
}

return deviceVendorData, &fleetdb.Attributes{
Namespace: constants.ServerVendorAttributeNS,
Data: deviceVendorDataBytes,
}, nil
}

// attributeByNamespace returns the attribute in the slice that matches the namespace
func attributeByNamespace(ns string, attributes []fleetdb.Attributes) *fleetdb.Attributes {
for _, attribute := range attributes {
if attribute.Namespace == ns {
return &attribute
}
}

return nil
}
116 changes: 116 additions & 0 deletions internal/fleetdb/fleetdb.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package internalfleetdb

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

"github.com/google/uuid"
"github.com/metal-toolbox/component-inventory/internal/app"
"github.com/metal-toolbox/component-inventory/pkg/api/constants"
"github.com/metal-toolbox/component-inventory/pkg/api/types"
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.ComponentInventoryDevice, *zap.Logger) error
UpdateServerBIOSConfig() error
}

// Creates a new Client, with reasonable defaults
func NewFleetDBClient(cfg *app.Configuration) (Client, error) {
client, err := fleetdb.NewClient(cfg.FleetDBAddress, nil)
if err != nil {
return nil, err
}

if cfg.FleetDBToken != "" {
client.SetToken(cfg.FleetDBToken)
}

return &fleetDBClient{
client: client,
}, nil
}

type fleetDBClient struct {
client *fleetdb.Client
}

func (fc fleetDBClient) GetServer(ctx context.Context, id uuid.UUID) (*fleetdb.Server, *fleetdb.ServerResponse, error) {
return fc.client.Get(ctx, id)
}

func (fc fleetDBClient) GetComponents(ctx context.Context, id uuid.UUID, params *fleetdb.PaginationParams) (fleetdb.ServerComponentSlice, *fleetdb.ServerResponse, error) {
return fc.client.GetComponents(ctx, id, params)
}

func (fc fleetDBClient) UpdateAttributes(ctx context.Context, server *fleetdb.Server, dev *types.ComponentInventoryDevice, 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.ComponentInventoryDevice, log *zap.Logger) error {
newVendorData, newVendorAttrs, err := deviceVendorAttributes(dev)
if err != nil {
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)

return err
}

return nil
}

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

func createUpdateServerBIOSConfig() error {
return fmt.Errorf("unimplemented")
}
11 changes: 6 additions & 5 deletions pkg/api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (
"fmt"
"net/http"

"github.com/metal-toolbox/component-inventory/pkg/api/constants"

"github.com/bmc-toolbox/common"
"github.com/metal-toolbox/component-inventory/pkg/api/routes"
rivets "github.com/metal-toolbox/rivets/types"
)

Expand Down Expand Up @@ -51,7 +52,7 @@ func NewClient(serverAddress string, opts ...Option) (Client, error) {
}

func (c componentInventoryClient) GetServerComponents(ctx context.Context, serverID string) (ServerComponents, error) {
path := fmt.Sprintf("%v/%v", routes.ComponentsEndpoint, serverID)
path := fmt.Sprintf("%v/%v", constants.ComponentsEndpoint, serverID)
resp, err := c.get(ctx, path)
if err != nil {
return nil, err
Expand All @@ -66,7 +67,7 @@ func (c componentInventoryClient) GetServerComponents(ctx context.Context, serve
}

func (c componentInventoryClient) Version(ctx context.Context) (string, error) {
resp, err := c.get(ctx, routes.VersionEndpoint)
resp, err := c.get(ctx, constants.VersionEndpoint)
if err != nil {
return "", err
}
Expand All @@ -75,7 +76,7 @@ func (c componentInventoryClient) Version(ctx context.Context) (string, error) {
}

func (c componentInventoryClient) UpdateInbandInventory(ctx context.Context, serverID string, device *common.Device) (string, error) {
path := fmt.Sprintf("%v/%v", routes.InbandInventoryEndpoint, serverID)
path := fmt.Sprintf("%v/%v", constants.InbandInventoryEndpoint, serverID)
body, err := json.Marshal(device)
if err != nil {
return "", fmt.Errorf("failed to parse device: %v", err)
Expand All @@ -90,7 +91,7 @@ func (c componentInventoryClient) UpdateInbandInventory(ctx context.Context, ser
}

func (c componentInventoryClient) UpdateOutOfbandInventory(ctx context.Context, serverID string, device *common.Device) (string, error) {
path := fmt.Sprintf("%v/%v", routes.OutofbandInventoryEndpoint, serverID)
path := fmt.Sprintf("%v/%v", constants.OutofbandInventoryEndpoint, serverID)
body, err := json.Marshal(device)
if err != nil {
return "", fmt.Errorf("failed to parse device: %v", err)
Expand Down
36 changes: 36 additions & 0 deletions pkg/api/constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package constants

const (
LivenessEndpoint = "/_health/liveness"
VersionEndpoint = "/api/version"
ComponentsEndpoint = "/components"
InbandInventoryEndpoint = "/inventory/in-band"
OutofbandInventoryEndpoint = "/inventory/out-of-band"

// server service attribute to look up the BMC IP Address in
BmcAttributeNamespace = "sh.hollow.bmc_info"

// server server service BMC address attribute key found under the bmcAttributeNamespace
BmcIPAddressAttributeKey = "address"

// serverservice namespace prefix the data is stored in.
ServerServiceNSPrefix = "sh.hollow.alloy"

// server vendor, model attributes are stored in this namespace.
ServerVendorAttributeNS = ServerServiceNSPrefix + ".server_vendor_attributes"

// additional server metadata are stored in this namespace.
ServerMetadataAttributeNS = ServerServiceNSPrefix + ".server_metadata_attributes"

// errors that occurred when connecting/collecting inventory from the bmc are stored here.
ServerBMCErrorsAttributeNS = ServerServiceNSPrefix + ".server_bmc_errors"

// server service server serial attribute key
ServerSerialAttributeKey = "serial"

// server service server model attribute key
ServerModelAttributeKey = "model"

// server service server vendor attribute key
ServerVendorAttributeKey = "vendor"
)
3 changes: 2 additions & 1 deletion pkg/api/routes/components.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"time"

"github.com/google/uuid"
internalfleetdb "github.com/metal-toolbox/component-inventory/internal/fleetdb"
fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1"
rdb "github.com/metal-toolbox/rivets/fleetdb"
rivets "github.com/metal-toolbox/rivets/types"
Expand All @@ -16,7 +17,7 @@ var fleetDBTimeout = 3 * time.Minute
// this is a map of "component_type_name" to the actual inventory data for each component
type serverComponents map[string][]*rivets.Component

func fetchServerComponents(client *fleetdb.Client, srvid uuid.UUID, l *zap.Logger) (serverComponents, error) {
func fetchServerComponents(client internalfleetdb.Client, srvid uuid.UUID, l *zap.Logger) (serverComponents, error) {
ctx, cancel := context.WithTimeout(context.Background(), fleetDBTimeout)
defer cancel()

Expand Down
9 changes: 6 additions & 3 deletions pkg/api/routes/components_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package routes

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

"encoding/json"
"fmt"
Expand Down Expand Up @@ -90,9 +91,10 @@ func TestFetchServerComponents(t *testing.T) {

logger := app.GetLogger(true)

client := getFleetDBClient(&app.Configuration{
client, err := internalfleetdb.NewFleetDBClient(&app.Configuration{
FleetDBAddress: ts.URL,
})
require.NoError(t, err)

result, err := fetchServerComponents(client, serverUUID, logger)
require.NoError(t, err)
Expand All @@ -118,11 +120,12 @@ func TestFetchServerComponents(t *testing.T) {

logger := app.GetLogger(true)

client := getFleetDBClient(&app.Configuration{
client, err := internalfleetdb.NewFleetDBClient(&app.Configuration{
FleetDBAddress: ts.URL,
})
require.NoError(t, err)

_, err := fetchServerComponents(client, serverUUID, logger)
_, err = fetchServerComponents(client, serverUUID, logger)
require.Error(t, err)
var srvErr fleetdb.ServerError
require.ErrorAs(t, err, &srvErr, "unexpected error type")
Expand Down
9 changes: 0 additions & 9 deletions pkg/api/routes/endpoints.go

This file was deleted.

16 changes: 10 additions & 6 deletions pkg/api/routes/inventory.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
package routes

import (
"context"
"errors"

"github.com/bmc-toolbox/common"
"github.com/google/uuid"
internalfleetdb "github.com/metal-toolbox/component-inventory/internal/fleetdb"
"github.com/metal-toolbox/component-inventory/pkg/api/types"
fleetdb "github.com/metal-toolbox/fleetdb/pkg/api/v1"
"go.uber.org/zap"
)

func processInband(c *fleetdb.Client, srvID uuid.UUID, dev *common.Device, log *zap.Logger) error { //nolint
log.Info("processing", zap.String("server id", srvID.String()), zap.String("device", dev.Serial))
func processInband(ctx context.Context, c internalfleetdb.Client, server *fleetdb.Server, dev *types.ComponentInventoryDevice, 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 {
return err
}
return errors.New("not implemented")
}

func processOutofband(c *fleetdb.Client, srvID uuid.UUID, dev *common.Device, log *zap.Logger) error { //nolint
log.Info("processing", zap.String("server id", srvID.String()), zap.String("device", dev.Serial))
func processOutofband(ctx context.Context, c internalfleetdb.Client, server *fleetdb.Server, dev *types.ComponentInventoryDevice, log *zap.Logger) error { //nolint
log.Info("processing", zap.String("server", server.Name), zap.String("device", dev.Inv.Serial))
return errors.New("not implemented")
}
Loading
Loading