From dbca2b8bc7ddf0f4aa222707f097e9ce48831b2b Mon Sep 17 00:00:00 2001 From: Alva8756 Date: Mon, 17 Jun 2024 23:06:41 -0700 Subject: [PATCH] fix Alloy backward compatibility issue --- internal/inventory/component_attributes.go | 6 +- internal/inventory/device_components.go | 40 ++++-- ...ver_invectory_backward_compability_test.go | 120 ++++++++++++++++++ 3 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 pkg/api/v1/router_server_invectory_backward_compability_test.go diff --git a/internal/inventory/component_attributes.go b/internal/inventory/component_attributes.go index 1eca1d3..d294a0d 100644 --- a/internal/inventory/component_attributes.go +++ b/internal/inventory/component_attributes.go @@ -18,12 +18,12 @@ func mustAttributesJSON(ca *rivets.ComponentAttributes) []byte { return byt } -func componentAttributesFromJSON(byt []byte) (*rivets.ComponentAttributes, error) { +func componentAttributesFromJSON(byt []byte) ([]rivets.ComponentAttributes, error) { if byt == nil { return nil, nil } - ca := &rivets.ComponentAttributes{} - if err := json.Unmarshal(byt, ca); err != nil { + var ca []rivets.ComponentAttributes + if err := json.Unmarshal(byt, &ca); err != nil { return nil, err } return ca, nil diff --git a/internal/inventory/device_components.go b/internal/inventory/device_components.go index 27e34cf..7636e42 100644 --- a/internal/inventory/device_components.go +++ b/internal/inventory/device_components.go @@ -119,7 +119,7 @@ func composeRecords(ctx context.Context, exec boil.ContextExecutor, cmp *rivets. } func retrieveComponentAttributes(ctx context.Context, exec boil.ContextExecutor, - componentID, namespace string) (*rivets.ComponentAttributes, error) { + componentID, namespace string) ([]rivets.ComponentAttributes, error) { ar, err := models.Attributes( models.AttributeWhere.ServerComponentID.EQ(null.StringFrom(componentID)), models.AttributeWhere.Namespace.EQ(namespace), @@ -213,7 +213,7 @@ func componentsFromDatabase(ctx context.Context, exec boil.ContextExecutor, for _, rec := range records { // attributes/firmware/status might not be stored because it was missing in the original data. - attr, err := retrieveComponentAttributes(ctx, exec, rec.ID, getAttributeNamespace(inband)) + attrs, err := retrieveComponentAttributes(ctx, exec, rec.ID, getAttributeNamespace(inband)) if err != nil { return nil, errors.Wrap(err, "retrieving "+rec.Name.String+"-"+rec.ID+" attributes"+":"+err.Error()) } @@ -231,16 +231,34 @@ func componentsFromDatabase(ctx context.Context, exec boil.ContextExecutor, default: return nil, errors.Wrap(err, "retrieving "+rec.Name.String+"-"+rec.ID+" status"+":"+err.Error()) } - comp := &rivets.Component{ - Name: rec.Name.String, - Vendor: rec.Vendor.String, - Model: rec.Model.String, - Serial: rec.Serial.String, - Firmware: fw, - Status: st, - Attributes: attr, + + if len(attrs) == 0 { + comp := &rivets.Component{ + Name: rec.Name.String, + Vendor: rec.Vendor.String, + Model: rec.Model.String, + Serial: rec.Serial.String, + Firmware: fw, + Status: st, + } + comps = append(comps, comp) + continue + } + + // Alternative: + // Changing rivets.Component to have []*ComponentAttributes instead of *ComponentAttributes + for _, attr := range attrs { + comp := &rivets.Component{ + Name: rec.Name.String, + Vendor: rec.Vendor.String, + Model: rec.Model.String, + Serial: rec.Serial.String, + Firmware: fw, + Status: st, + Attributes: &attr, + } + comps = append(comps, comp) } - comps = append(comps, comp) } return comps, nil diff --git a/pkg/api/v1/router_server_invectory_backward_compability_test.go b/pkg/api/v1/router_server_invectory_backward_compability_test.go new file mode 100644 index 0000000..4f6543f --- /dev/null +++ b/pkg/api/v1/router_server_invectory_backward_compability_test.go @@ -0,0 +1,120 @@ +package fleetdbapi_test + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/metal-toolbox/fleetdb/internal/dbtools" + fleetdbapi "github.com/metal-toolbox/fleetdb/pkg/api/v1" +) + +func TestIntegrationServerCreateComponentsGetInventory(t *testing.T) { + s := serverTest(t) + + realClientTests(t, func(ctx context.Context, authToken string, _ int, expectError bool) error { + s.Client.SetToken(authToken) + + attrs, _, err := s.Client.GetComponents(ctx, uuid.MustParse(dbtools.FixtureNemo.ID), nil) + if !expectError { + require.NoError(t, err) + assert.Len(t, attrs, 2) + } + + return err + }) + + // init fixture data + // 1. get list of servers + servers, _, err := s.Client.List(context.Background(), nil) + if err != nil { + t.Fatal(err) + } + + // expect atleast 1 server for test to proceed + assert.GreaterOrEqual(t, len(servers), 1) + + // 2. get component type slice + componentTypeSlice, _, err := s.Client.ListServerComponentTypes(context.Background(), nil) + if err != nil { + t.Fatal(err) + } + + // expect atleast 1 component type to proceed + assert.GreaterOrEqual(t, len(componentTypeSlice), 1) + + expectedAttribute1 := `{"id":"Enclosure.Internal.0-1","chassis_type":"StorageEnclosure","description":"PCIe SSD Backplane 1"}` + expectedAttribute2 := `{"id":"NIC.Embedded.2-1-1","physid":"2"}` + expectedVAStatus := `{"Health":"OK","State":"Enabled"}` + + metadataData := fmt.Sprintf("[%v,%v]", expectedAttribute1, expectedAttribute2) + // `[{"chassis_type":"StorageEnclosure","description":"PCIe SSD Backplane 1","id":"Enclosure.Internal.0-1"},{"id":"NIC.Embedded.2-1-1","physid":"2"}]` + vaStatus := fmt.Sprintf(`[ + {"status":%v},{"nic_port_status":{"Health":"OK","State":"Enabled","active_link_technology": + "Ethernet","id":"NIC.Slot.3-2-1","link_status":"Up","macaddress":"40:A6:B7:5C:E5:61"}},{"nic_port_status":{"Health":"OK","State":"Enabled","active_link_technology":"Ethernet","id":"NIC.Slot.3-1-1","link_status":"Up","macaddress":"40:A6:B7:5C:E5:60"}} + ]`, expectedVAStatus) + + // fixture to create a server components + csFixtureCreate := fleetdbapi.ServerComponentSlice{ + { + ServerUUID: servers[1].UUID, + Name: "fakeName", + Vendor: "fakeVendor", + Model: "fakeModel", + Serial: "fakeSerial", + ComponentTypeID: componentTypeSlice.ByName("Fins").ID, + ComponentTypeName: componentTypeSlice.ByName("Fins").Name, + ComponentTypeSlug: componentTypeSlice.ByName("Fins").Slug, + Attributes: []fleetdbapi.Attributes{ + { + Namespace: "sh.hollow.alloy.outofband.metadata", + Data: json.RawMessage([]byte(metadataData)), + }, + }, + VersionedAttributes: []fleetdbapi.VersionedAttributes{ + { + Namespace: "sh.hollow.alloy.outofband.status", + Data: json.RawMessage([]byte(vaStatus)), + }, + }, + }, + } + + // create server component + _, err = s.Client.CreateComponents(context.TODO(), servers[1].UUID, csFixtureCreate) + if err != nil { + t.Fatal(err) + } + + var gotAttribute1 bool + var gotAttribute2 bool + got, _, err := s.Client.GetServerInventory(context.TODO(), servers[1].UUID, false) + for _, c := range got.Components { + if c.Name == "fakeName" && c.Vendor == "fakeVendor" && c.Model == "fakeModel" && c.Serial == "fakeSerial" { + gotVAStatus, _ := json.Marshal(c.Status) + if string(gotVAStatus) != expectedVAStatus { + t.Errorf("got VA status %v, expect %v", string(gotVAStatus), expectedVAStatus) + } + + gotMetadataData, _ := json.Marshal(c.Attributes) + fmt.Printf("string(gotMetadataData) = %v\n", string(gotMetadataData)) + if string(gotMetadataData) == expectedAttribute1 { + gotAttribute1 = true + } + if string(gotMetadataData) == expectedAttribute2 { + gotAttribute2 = true + } + } + } + if !gotAttribute1 { + t.Errorf("failed to receive metadata %v", expectedAttribute1) + } + if !gotAttribute2 { + t.Errorf("failed to receive metadata %v", expectedAttribute2) + } +}