From 32a5f82665c065b9c0353722f94874f8b6ef34d5 Mon Sep 17 00:00:00 2001 From: Manuel Kieweg <mail@manuelkieweg.de> Date: Fri, 19 Feb 2021 15:21:17 +0000 Subject: [PATCH] New storage concept drafted and tested --- mocks/PrincipalNetworkDomain.go | 207 ++++++++++++ mocks/SouthboundInterface.go | 18 + mocks/Storable.go | 30 ++ nucleus/cli-handling.go | 68 +++- nucleus/clientConfig.go | 2 +- nucleus/controller.go | 25 +- nucleus/controller_test.go | 85 ----- nucleus/device.go | 4 + nucleus/errors.go | 9 + nucleus/gnmi_transport_test.go | 2 + nucleus/principalNetworkDomain.go | 135 ++++---- nucleus/principalNetworkDomain_test.go | 219 +++++++++--- nucleus/southbound.go | 42 +-- nucleus/store.go | 107 ++++++ nucleus/store_test.go | 448 +++++++++++++++++++++++++ 15 files changed, 1138 insertions(+), 263 deletions(-) create mode 100644 mocks/PrincipalNetworkDomain.go create mode 100644 mocks/Storable.go create mode 100644 nucleus/store.go create mode 100644 nucleus/store_test.go diff --git a/mocks/PrincipalNetworkDomain.go b/mocks/PrincipalNetworkDomain.go new file mode 100644 index 000000000..8796a000b --- /dev/null +++ b/mocks/PrincipalNetworkDomain.go @@ -0,0 +1,207 @@ +// Code generated by mockery v2.6.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + uuid "github.com/google/uuid" +) + +// PrincipalNetworkDomain is an autogenerated mock type for the PrincipalNetworkDomain type +type PrincipalNetworkDomain struct { + mock.Mock +} + +// AddDevice provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) AddDevice(_a0 interface{}) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// AddSbi provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) AddSbi(_a0 interface{}) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(interface{}) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// ContainsDevice provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) ContainsDevice(_a0 uuid.UUID) bool { + ret := _m.Called(_a0) + + var r0 bool + if rf, ok := ret.Get(0).(func(uuid.UUID) bool); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// Destroy provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) Destroy() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetDescription provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) GetDescription() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetName provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) GetName() string { + ret := _m.Called() + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetSBIs provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) GetSBIs() interface{} { + ret := _m.Called() + + var r0 interface{} + if rf, ok := ret.Get(0).(func() interface{}); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(interface{}) + } + } + + return r0 +} + +// Id provides a mock function with given fields: +func (_m *PrincipalNetworkDomain) Id() uuid.UUID { + ret := _m.Called() + + var r0 uuid.UUID + if rf, ok := ret.Get(0).(func() uuid.UUID); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(uuid.UUID) + } + } + + return r0 +} + +// MarshalDevice provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) MarshalDevice(_a0 uuid.UUID) (string, error) { + ret := _m.Called(_a0) + + var r0 string + if rf, ok := ret.Get(0).(func(uuid.UUID) string); ok { + r0 = rf(_a0) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(uuid.UUID) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RemoveDevice provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) RemoveDevice(_a0 uuid.UUID) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(uuid.UUID) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveSbi provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) RemoveSbi(_a0 uuid.UUID) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(uuid.UUID) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Request provides a mock function with given fields: _a0, _a1 +func (_m *PrincipalNetworkDomain) Request(_a0 uuid.UUID, _a1 string) error { + ret := _m.Called(_a0, _a1) + + var r0 error + if rf, ok := ret.Get(0).(func(uuid.UUID, string) error); ok { + r0 = rf(_a0, _a1) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RequestAll provides a mock function with given fields: _a0 +func (_m *PrincipalNetworkDomain) RequestAll(_a0 string) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/mocks/SouthboundInterface.go b/mocks/SouthboundInterface.go index ecb98e044..e4e0d4c16 100644 --- a/mocks/SouthboundInterface.go +++ b/mocks/SouthboundInterface.go @@ -6,6 +6,8 @@ import ( gnmi "github.com/openconfig/gnmi/proto/gnmi" mock "github.com/stretchr/testify/mock" + uuid "github.com/google/uuid" + yang "github.com/openconfig/goyang/pkg/yang" ytypes "github.com/openconfig/ygot/ytypes" @@ -16,6 +18,22 @@ type SouthboundInterface struct { mock.Mock } +// Id provides a mock function with given fields: +func (_m *SouthboundInterface) Id() uuid.UUID { + ret := _m.Called() + + var r0 uuid.UUID + if rf, ok := ret.Get(0).(func() uuid.UUID); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(uuid.UUID) + } + } + + return r0 +} + // SbiIdentifier provides a mock function with given fields: func (_m *SouthboundInterface) SbiIdentifier() string { ret := _m.Called() diff --git a/mocks/Storable.go b/mocks/Storable.go new file mode 100644 index 000000000..1d2dc7d99 --- /dev/null +++ b/mocks/Storable.go @@ -0,0 +1,30 @@ +// Code generated by mockery v2.6.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + uuid "github.com/google/uuid" +) + +// Storable is an autogenerated mock type for the Storable type +type Storable struct { + mock.Mock +} + +// Id provides a mock function with given fields: +func (_m *Storable) Id() uuid.UUID { + ret := _m.Called() + + var r0 uuid.UUID + if rf, ok := ret.Get(0).(func() uuid.UUID); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(uuid.UUID) + } + } + + return r0 +} diff --git a/nucleus/cli-handling.go b/nucleus/cli-handling.go index a3cd14a7a..6ad6b730b 100644 --- a/nucleus/cli-handling.go +++ b/nucleus/cli-handling.go @@ -142,21 +142,45 @@ func getCLIGoing(core *Core) { // the core func (s *server) CreatePND(ctx context.Context, in *pb.CreatePNDRequest) (*pb.CreatePNDReply, error) { log.Info("Received: Create a PND with the name", in.GetName()) - sbi := s.core.southboundInterfaces[in.GetSbi()] + sbi, err := s.core.sbic.get(uuid.New()) + if err != nil { + return nil, err + } id := uuid.New() - s.core.principalNetworkDomains[id] = NewPND(in.GetName(), in.GetDescription(), sbi) + pnd, err := NewPNDwithId(in.GetName(), in.GetDescription(), id, sbi.(SouthboundInterface)) + if err != nil { + log.Error(err) + return &pb.CreatePNDReply{Message: err.Error()}, err + } + if err := s.core.pndc.add(pnd); err != nil { + return nil, err + } return &pb.CreatePNDReply{Message: "Created new PND: " + id.String()}, nil } +// deprecated +// Subject to change, using discontinued full device access // GetAllPNDs is a request to get all currently registered PNDs and returns a slim // variant of PNDs and their respective devices func (s *server) GetAllPNDs(ctx context.Context, in *emptypb.Empty) (*pb.AllPNDsReply, error) { log.Info("Received: Get all PNDs") var pnds []*pb.PND - for uuidPND, pnd := range s.core.principalNetworkDomains { + for _, uuidPND := range s.core.pndc.UUIDs() { + pnd, err := s.core.pndc.get(uuidPND) + if err != nil { + log.Error(err) + continue + } var devices []*pb.Device - for uuidDevice, device := range pnd.(*pndImplementation).devices { + for uuidDevice, d := range pnd.(*pndImplementation).devices.store { + device, ok := d.(*Device) + if !ok { + log.Error(&ErrInvalidTypeAssertion{ + v: d, + t: "Device", + }) + } tmpDevice := pb.Device{ Uuid: uuidDevice.String(), Address: device.Config.Address, @@ -164,11 +188,16 @@ func (s *server) GetAllPNDs(ctx context.Context, in *emptypb.Empty) (*pb.AllPNDs Password: device.Config.Password} devices = append(devices, &tmpDevice) } + sbi, err := s.core.sbic.get(pnd.GetSBIs().(*sbiStore).UUIDs()[0]) + if err != nil { + log.Error(err) + continue + } tmpPND := pb.PND{ Uuid: uuidPND.String(), Name: pnd.GetName(), Description: pnd.GetDescription(), - Sbi: pnd.GetSBIs()["default"].SbiIdentifier(), + Sbi: sbi.SbiIdentifier(), Devices: devices, } pnds = append(pnds, &tmpPND) @@ -179,7 +208,12 @@ func (s *server) GetAllPNDs(ctx context.Context, in *emptypb.Empty) (*pb.AllPNDs // GetAllSBINames returns all registered SBIs from core. func (s *server) GetAllSBINames(ctx context.Context, in *emptypb.Empty) (*pb.AllSBINamesReply, error) { var sbiNames []string - for _, s := range s.core.southboundInterfaces { + for _, uuidDevice := range s.core.sbic.UUIDs() { + s, err := s.core.sbic.get(uuidDevice) + if err != nil { + log.Error(err) + continue + } sbiNames = append(sbiNames, s.SbiIdentifier()) } return &pb.AllSBINamesReply{SbiNames: sbiNames}, nil @@ -193,12 +227,18 @@ func (s *server) AddDevice(ctx context.Context, in *pb.AddDeviceRequest) (*pb.Ad if err != nil { return &pb.AddDeviceReply{Message: err.Error()}, err } - pnd, exists := s.core.principalNetworkDomains[uuidPND] - if exists != true { - log.Info(err) + pnd, err := s.core.pndc.get(uuidPND) + if err != nil { + log.Error(err) + return &pb.AddDeviceReply{Message: err.Error()}, err + } + // TODO: Add notion of default SBI to PND or solve differently + uuidSbi := pnd.GetSBIs().(*sbiStore).UUIDs()[0] + sbi, err := s.core.sbic.get(uuidSbi) + if err != nil { + log.Error(err) return &pb.AddDeviceReply{Message: err.Error()}, err } - sbi := s.core.principalNetworkDomains[uuidPND].GetSBIs()["default"] //TODO: could the transport and the related config be created in device? transport := &Gnmi{SetNode: sbi.SetNode()} @@ -215,14 +255,14 @@ func (s *server) AddDevice(ctx context.Context, in *pb.AddDeviceRequest) (*pb.Ad err = pnd.AddDevice(newDevice) if err != nil { - log.Info(err) + log.Error(err) return &pb.AddDeviceReply{Message: err.Error()}, err } return &pb.AddDeviceReply{Message: "Added new Device: " + newDevice.Config.Uuid.String()}, err } -// HandleDeviceGetRequest handles a GET request via pnd.Request() +// HandleDeviceGetRequest handles a GET request via newPnd.Request() func (s *server) HandleDeviceGetRequest(ctx context.Context, in *pb.DeviceGetRequest) (*pb.DeviceGetReply, error) { log.Info("Received: HandleDeviceGetRequest") uuidPND, err := uuid.Parse(in.GetUuidPND()) @@ -235,8 +275,8 @@ func (s *server) HandleDeviceGetRequest(ctx context.Context, in *pb.DeviceGetReq log.Info(err) return &pb.DeviceGetReply{Message: err.Error()}, err } - pnd, exists := s.core.principalNetworkDomains[uuidPND] - if exists != true { + pnd, err := s.core.pndc.get(uuidPND) + if err != nil { err := errors.New("Couldnt find PND: UUID is wrong") log.Info(err) return &pb.DeviceGetReply{Message: err.Error()}, err diff --git a/nucleus/clientConfig.go b/nucleus/clientConfig.go index 297e7a7fe..c88b951fc 100644 --- a/nucleus/clientConfig.go +++ b/nucleus/clientConfig.go @@ -1,6 +1,6 @@ package nucleus -// ClientConfig contains SBI ciena +// ClientConfig contains SBI client // configuration parameters // Deprecated in favor of spf viper type ClientConfig struct { diff --git a/nucleus/controller.go b/nucleus/controller.go index 21ef57b7b..6c237be25 100644 --- a/nucleus/controller.go +++ b/nucleus/controller.go @@ -4,33 +4,28 @@ import ( "os" "code.fbi.h-da.de/cocsn/gosdn/database" - "github.com/google/uuid" log "github.com/sirupsen/logrus" "github.com/spf13/viper" ) // Core is the representation of the controllers core type Core struct { - // deprecated, use sbiStore instead - southboundInterfaces map[string]SouthboundInterface - // deprecated, use pndStore instead - principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain // deprecated database database.Database - pndStore pndStorage - sbiStore sbiStorage + pndc pndStore + sbic sbiStore IsRunning chan bool } //Initialize does start-up housekeeping like reading controller config files func (c *Core) Initialize(IsRunningChannel chan bool) error { - c.sbiStore = sbiStorage{} - c.pndStore = pndStorage{} - - // TODO: Just for compatability remove once deprecated code is cleaned up - c.southboundInterfaces = c.sbiStore - c.principalNetworkDomains = c.pndStore + c.sbic = sbiStore{ + store{}, + } + c.pndc = pndStore{ + store{}, + } // Set config defaults viper.SetDefault("socket", "localhost:55055") @@ -61,10 +56,10 @@ func (c *Core) AttachDatabase() { // CreateSouthboundInterfaces initializes the controller with its supported SBIs func (c *Core) CreateSouthboundInterfaces() error { - if err := c.sbiStore.add(&AristaOC{}); err != nil { + if err := c.sbic.add(&AristaOC{}); err != nil { return err } - if err := c.sbiStore.add(&OpenConfig{}); err != nil { + if err := c.sbic.add(&OpenConfig{}); err != nil { return err } return nil diff --git a/nucleus/controller_test.go b/nucleus/controller_test.go index dd2a0175a..0fa4ffa90 100644 --- a/nucleus/controller_test.go +++ b/nucleus/controller_test.go @@ -1,86 +1 @@ package nucleus - -import ( - "code.fbi.h-da.de/cocsn/gosdn/database" - "github.com/google/uuid" - "testing" -) - -func TestCore_CreateSouthboundInterfaces(t *testing.T) { - type fields struct { - southboundInterfaces map[string]SouthboundInterface - principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain - database database.Database - IsRunning chan bool - } - tests := []struct { - name string - fields fields - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _ = &Core{ - southboundInterfaces: tt.fields.southboundInterfaces, - principalNetworkDomains: tt.fields.principalNetworkDomains, - database: tt.fields.database, - IsRunning: tt.fields.IsRunning, - } - }) - } -} - -func TestCore_Initialize(t *testing.T) { - type fields struct { - southboundInterfaces map[string]SouthboundInterface - principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain - database database.Database - IsRunning chan bool - } - type args struct { - IsRunningChannel chan bool - } - tests := []struct { - name string - fields fields - args args - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _ = &Core{ - southboundInterfaces: tt.fields.southboundInterfaces, - principalNetworkDomains: tt.fields.principalNetworkDomains, - database: tt.fields.database, - IsRunning: tt.fields.IsRunning, - } - }) - } -} - -func TestCore_Shutdown(t *testing.T) { - type fields struct { - southboundInterfaces map[string]SouthboundInterface - principalNetworkDomains map[uuid.UUID]PrincipalNetworkDomain - database database.Database - IsRunning chan bool - } - tests := []struct { - name string - fields fields - }{ - // TODO: Add test cases. - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - _ = &Core{ - southboundInterfaces: tt.fields.southboundInterfaces, - principalNetworkDomains: tt.fields.principalNetworkDomains, - database: tt.fields.database, - IsRunning: tt.fields.IsRunning, - } - }) - } -} diff --git a/nucleus/device.go b/nucleus/device.go index dcad6b388..24007fd30 100644 --- a/nucleus/device.go +++ b/nucleus/device.go @@ -35,6 +35,10 @@ func NewDevice(sbi SouthboundInterface, addr, username, password string, } } +func (d *Device) Id() uuid.UUID { + return d.Config.Uuid +} + type DeviceConfig struct { Uuid uuid.UUID Address string diff --git a/nucleus/errors.go b/nucleus/errors.go index 67ebde4ed..ce23a6d6f 100644 --- a/nucleus/errors.go +++ b/nucleus/errors.go @@ -24,3 +24,12 @@ type ErrAlreadyExists struct { func (e *ErrAlreadyExists) Error() string { return fmt.Sprintf("%v already exists", e.item) } + +type ErrInvalidTypeAssertion struct { + v interface{} + t interface{} +} + +func (e ErrInvalidTypeAssertion) Error() string { + return fmt.Sprintf("%v does not implement %v", e.v, e.t) +} diff --git a/nucleus/gnmi_transport_test.go b/nucleus/gnmi_transport_test.go index ba05a018a..b36a4a542 100644 --- a/nucleus/gnmi_transport_test.go +++ b/nucleus/gnmi_transport_test.go @@ -19,9 +19,11 @@ import ( ) // TestMain bootstraps all tests. Humongous beast +// TODO: Move somewhere more sensible func TestMain(m *testing.M) { testSetupGnmi() testSetupPnd() + testSetupStore() os.Exit(m.Run()) } diff --git a/nucleus/principalNetworkDomain.go b/nucleus/principalNetworkDomain.go index 2ed330115..7b26ada3e 100644 --- a/nucleus/principalNetworkDomain.go +++ b/nucleus/principalNetworkDomain.go @@ -11,9 +11,9 @@ import ( // interface for PND implementations type PrincipalNetworkDomain interface { Destroy() error - AddSbi(SouthboundInterface) error - RemoveSbi(string) error - AddDevice(*Device) error + AddSbi(interface{}) error + RemoveSbi(uuid.UUID) error + AddDevice(interface{}) error RemoveDevice(uuid.UUID) error Request(uuid.UUID, string) error RequestAll(string) error @@ -21,28 +21,48 @@ type PrincipalNetworkDomain interface { GetDescription() string MarshalDevice(uuid.UUID) (string, error) ContainsDevice(uuid.UUID) bool - GetSBIs() map[string]SouthboundInterface + GetSBIs() interface{} + Id() uuid.UUID } type pndImplementation struct { name string description string - sbi map[string]SouthboundInterface - devices map[uuid.UUID]*Device + sbic sbiStore + devices deviceStore id uuid.UUID } // NewPND creates a Principle Network Domain -func NewPND(name, description string, sbi SouthboundInterface) PrincipalNetworkDomain { - sbic := make(map[string]SouthboundInterface) - sbic["default"] = sbi - devices := make(map[uuid.UUID]*Device) - return &pndImplementation{ +func NewPND(name, description string, sbi SouthboundInterface) (PrincipalNetworkDomain, error) { + pnd := &pndImplementation{ name: name, description: description, - sbi: sbic, - devices: devices, + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, } + if err := pnd.sbic.add(sbi); err != nil { + return nil, &ErrAlreadyExists{item: sbi} + } + return pnd, nil +} + +func NewPNDwithId(name, description string, id uuid.UUID, sbi SouthboundInterface) (PrincipalNetworkDomain, error) { + pnd := &pndImplementation{ + name: name, + description: description, + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + id: id, + } + if err := pnd.sbic.add(sbi); err != nil { + return nil, &ErrAlreadyExists{item: sbi} + } + return pnd, nil +} + +func (pnd *pndImplementation) Id() uuid.UUID { + return pnd.id } // GetName returns the name of the PND @@ -51,9 +71,8 @@ func (pnd *pndImplementation) GetName() string { } // ContainsDevice checks if the given device uuid is registered for this PND -func (pnd *pndImplementation) ContainsDevice(uuid uuid.UUID) bool { - _, exists := pnd.devices[uuid] - return exists +func (pnd *pndImplementation) ContainsDevice(id uuid.UUID) bool { + return pnd.devices.exists(id) } // GetDescription returns the current description of the PND @@ -62,8 +81,8 @@ func (pnd *pndImplementation) GetDescription() string { } // GetSBIs returns the registered SBIs -func (pnd *pndImplementation) GetSBIs() map[string]SouthboundInterface { - return pnd.sbi +func (pnd *pndImplementation) GetSBIs() interface{} { + return &pnd.sbic } // Destroy destroys the PND @@ -72,21 +91,35 @@ func (pnd *pndImplementation) Destroy() error { } // AddSbi adds a SBI to the PND which will be supported -func (pnd *pndImplementation) AddSbi(sbi SouthboundInterface) error { - return pnd.addSbi(sbi) +func (pnd *pndImplementation) AddSbi(sbi interface{}) error { + s, ok := sbi.(SouthboundInterface) + if !ok { + return &ErrInvalidTypeAssertion{ + v: sbi, + t: "Device", + } + } + return pnd.addSbi(s) } // AddSbi removes a SBI from the PND // TODO: this should to recursivly through // devices and remove the devices using // this SBI -func (pnd *pndImplementation) RemoveSbi(sbiIdentifier string) error { - return pnd.removeSbi(sbiIdentifier) +func (pnd *pndImplementation) RemoveSbi(id uuid.UUID) error { + return pnd.removeSbi(id) } //AddDevice adds a new device to the PND -func (pnd *pndImplementation) AddDevice(device *Device) error { - return pnd.addDevice(device) +func (pnd *pndImplementation) AddDevice(device interface{}) error { + d, ok := device.(*Device) + if !ok { + return &ErrInvalidTypeAssertion{ + v: device, + t: "Device", + } + } + return pnd.addDevice(d) } // RemoveDevice removes a device from the PND @@ -101,31 +134,23 @@ func destroy() error { } func (pnd *pndImplementation) addSbi(sbi SouthboundInterface) error { - pnd.sbi[sbi.SbiIdentifier()] = sbi - return nil + return pnd.sbic.add(sbi) } -func (pnd *pndImplementation) removeSbi(sbiIdentifier string) error { - delete(pnd.sbi, sbiIdentifier) - return nil +func (pnd *pndImplementation) removeSbi(id uuid.UUID) error { + return pnd.sbic.delete(id) } func (pnd *pndImplementation) addDevice(device *Device) error { - pnd.devices[device.Config.Uuid] = device - return nil + return pnd.devices.add(device) } -func (pnd *pndImplementation) getDevice(uuid uuid.UUID) (*Device, error) { - d, ok := pnd.devices[uuid] - if !ok { - return nil, &ErrNotFound{id: uuid} - } - return d, nil +func (pnd *pndImplementation) getDevice(id uuid.UUID) (*Device, error) { + return pnd.devices.get(id) } -func (pnd *pndImplementation) removeDevice(uuid uuid.UUID) error { - delete(pnd.devices, uuid) - return nil +func (pnd *pndImplementation) removeDevice(id uuid.UUID) error { + return pnd.devices.delete(id) } func (pnd *pndImplementation) MarshalDevice(uuid uuid.UUID) (string, error) { @@ -161,38 +186,10 @@ func (pnd *pndImplementation) Request(uuid uuid.UUID, path string) error { //RequestAll sends a request for all registered devices func (pnd *pndImplementation) RequestAll(path string) error { - for k := range pnd.devices { + for _, k := range pnd.devices.UUIDs() { if err := pnd.Request(k, path); err != nil { return err } } return nil } - -type pndStorage map[uuid.UUID]PrincipalNetworkDomain - -func (p pndStorage) exists(id uuid.UUID) bool { - _, ok := p[id] - return ok -} - -func (p pndStorage) add(pnd PrincipalNetworkDomain) error { - // TODO: Implement duplicate detection. Changes PrincipalNetworkDomain API - p[uuid.New()] = pnd - return nil -} - -func (p pndStorage) Sbi(id uuid.UUID) (PrincipalNetworkDomain, error) { - if !p.exists(id) { - return nil, &ErrNotFound{id: id} - } - return p[id], nil -} - -func (p pndStorage) delete(id uuid.UUID) error { - if !p.exists(id) { - return &ErrNotFound{id: id} - } - delete(p, id) - return nil -} diff --git a/nucleus/principalNetworkDomain_test.go b/nucleus/principalNetworkDomain_test.go index 18dbf2dde..5d77a64dd 100644 --- a/nucleus/principalNetworkDomain_test.go +++ b/nucleus/principalNetworkDomain_test.go @@ -22,6 +22,16 @@ func testSetupPnd() { if err != nil { log.Fatal(err) } + + defaultSbiId, err = uuid.Parse("b70c8425-68c7-4d4b-bb5e-5586572bd64b") + if err != nil { + log.Fatal(err) + } + + defaultPndId, err = uuid.Parse("b4016412-eec5-45a1-aa29-f59915357bad") + if err != nil { + log.Fatal(err) + } } func mockDevice() Device { @@ -38,49 +48,114 @@ func mockDevice() Device { } } -func freshPnd() pndImplementation { +func newPnd() pndImplementation { return pndImplementation{ name: "default", description: "default test pnd", - sbi: map[string]SouthboundInterface{"default": &OpenConfig{}}, - devices: map[uuid.UUID]*Device{}, + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + } +} + +func newPndWithId() pndImplementation { + return pndImplementation{ + name: "default", + description: "default test pnd", + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + id: defaultPndId, } } var did uuid.UUID var mdid uuid.UUID +var defaultSbiId uuid.UUID +var defaultPndId uuid.UUID func TestNewPND(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() + if err := pnd.addSbi(&OpenConfig{id: defaultSbiId}); err != nil { + t.Error(err) + } type args struct { name string description string sbi SouthboundInterface } tests := []struct { - name string - args args - want PrincipalNetworkDomain + name string + args args + want PrincipalNetworkDomain + wantErr bool }{ { name: "default", args: args{ name: "default", description: "default test pnd", - sbi: &OpenConfig{}, + sbi: &OpenConfig{id: defaultSbiId}, }, - want: &pnd, + want: &pnd, + wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := NewPND(tt.args.name, tt.args.description, tt.args.sbi); !reflect.DeepEqual(got, tt.want) { + got, err := NewPND(tt.args.name, tt.args.description, tt.args.sbi) + if (err != nil) != tt.wantErr { + t.Errorf("NewPND() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { t.Errorf("NewPND() = %v, want %v", got, tt.want) } }) } } +func TestNewPNDwithId(t *testing.T) { + pnd := newPndWithId() + if err := pnd.addSbi(&OpenConfig{id: defaultSbiId}); err != nil { + t.Error(err) + } + type args struct { + name string + description string + id uuid.UUID + sbi SouthboundInterface + } + tests := []struct { + name string + args args + want PrincipalNetworkDomain + wantErr bool + }{ + { + name: "default", + args: args{ + name: "default", + description: "default test pnd", + id: defaultPndId, + sbi: &OpenConfig{id: defaultSbiId}, + }, + want: &pnd, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := NewPNDwithId(tt.args.name, tt.args.description, tt.args.id, tt.args.sbi) + if (err != nil) != tt.wantErr { + t.Errorf("NewPNDwithId() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("NewPNDwithId() got = %v, want %v", got, tt.want) + } + }) + } +} + func Test_destroy(t *testing.T) { tests := []struct { name string @@ -118,16 +193,18 @@ func Test_pndImplementation_AddDevice(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() if err := pnd.AddDevice(tt.args.device); (err != nil) != tt.wantErr { t.Errorf("AddDevice() error = %v, wantErr %v", err, tt.wantErr) } - _, ok := pnd.devices[did] + _, ok := pnd.devices.store[did] if !ok { t.Errorf("AddDevice() Device %v not in device store %v", tt.args.device, pnd.devices) } - delete(pnd.devices, did) + if err := pnd.devices.delete(did); err != nil { + t.Error(err) + } }) } } @@ -141,20 +218,30 @@ func Test_pndImplementation_AddSbi(t *testing.T) { args args wantErr bool }{ - {name: "default", args: args{sbi: &OpenConfig{}}, wantErr: false}, + { + name: "default", + args: args{ + sbi: &OpenConfig{ + id: defaultSbiId, + }, + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() if err := pnd.AddSbi(tt.args.sbi); (err != nil) != tt.wantErr { t.Errorf("AddSbi() error = %v, wantErr %v", err, tt.wantErr) } - _, ok := pnd.GetSBIs()[tt.args.sbi.SbiIdentifier()] + _, ok := pnd.sbic.store[defaultSbiId] if !ok { t.Errorf("AddSbi() SBI %v not in device store %v", tt.args.sbi, pnd.GetSBIs()) } - delete(pnd.sbi, tt.args.sbi.SbiIdentifier()) + if err := pnd.sbic.delete(defaultSbiId); err != nil { + t.Error(err) + } }) } } @@ -177,15 +264,25 @@ func Test_pndImplementation_ContainsDevice(t *testing.T) { uuid: uuid.New(), device: &Device{Config: DeviceConfig{Uuid: did}}, }, want: false}, + {name: "fails empty", args: args{ + uuid: uuid.New(), + device: &Device{Config: DeviceConfig{Uuid: did}}, + }, want: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() - pnd.devices[did] = tt.args.device + pnd := newPnd() + if tt.name != "fails empty" { + if err := pnd.devices.add(tt.args.device); err != nil { + t.Error(err) + } + } if got := pnd.ContainsDevice(tt.args.uuid); got != tt.want { t.Errorf("ContainsDevice() = %v, want %v", got, tt.want) } - delete(pnd.devices, did) + if err := pnd.devices.delete(did); err != nil && tt.name != "fails empty" { + t.Error(err) + } }) } } @@ -194,8 +291,8 @@ func Test_pndImplementation_Destroy(t *testing.T) { type fields struct { name string description string - sbi map[string]SouthboundInterface - devices map[uuid.UUID]*Device + sbi sbiStore + devices deviceStore } tests := []struct { name string @@ -209,7 +306,7 @@ func Test_pndImplementation_Destroy(t *testing.T) { pnd := &pndImplementation{ name: tt.fields.name, description: tt.fields.description, - sbi: tt.fields.sbi, + sbic: tt.fields.sbi, devices: tt.fields.devices, } if err := pnd.Destroy(); (err != nil) != tt.wantErr { @@ -228,7 +325,7 @@ func Test_pndImplementation_GetDescription(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() if got := pnd.GetDescription(); got != tt.want { t.Errorf("GetDescription() = %v, want %v", got, tt.want) } @@ -245,7 +342,7 @@ func Test_pndImplementation_GetName(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() if got := pnd.GetName(); got != tt.want { t.Errorf("GetName() = %v, want %v", got, tt.want) } @@ -254,12 +351,12 @@ func Test_pndImplementation_GetName(t *testing.T) { } func Test_pndImplementation_GetSBIs(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() tests := []struct { name string - want map[string]SouthboundInterface + want *sbiStore }{ - {name: "default", want: pnd.sbi}, + {name: "default", want: &pnd.sbic}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -284,7 +381,7 @@ func Test_pndImplementation_MarshalDevice(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() + pnd := newPnd() d := &Device{ GoStruct: &openconfig.Device{}, SBI: nil, @@ -296,7 +393,9 @@ func Test_pndImplementation_MarshalDevice(t *testing.T) { }, Transport: nil, } - _ = pnd.addDevice(d) + if err := pnd.addDevice(d); err != nil { + t.Error(err) + } got, err := pnd.MarshalDevice(tt.args.uuid) if (err != nil) != tt.wantErr { t.Errorf("MarshalDevice() error = %v, wantErr %v", err, tt.wantErr) @@ -305,7 +404,9 @@ func Test_pndImplementation_MarshalDevice(t *testing.T) { if got != tt.want { t.Errorf("MarshalDevice() got = %v, want %v", got, tt.want) } - delete(pnd.devices, did) + if err := pnd.devices.delete(did); err != nil { + t.Error(err) + } }) } } @@ -320,18 +421,23 @@ func Test_pndImplementation_RemoveDevice(t *testing.T) { wantErr bool }{ {name: "default", args: args{uuid: did}, wantErr: false}, + {name: "fails", args: args{uuid: uuid.New()}, wantErr: true}, + {name: "fails empty", args: args{uuid: did}, wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() - d := &Device{Config: DeviceConfig{Uuid: did}} - _ = pnd.addDevice(d) + pnd := newPnd() + if tt.name != "fails empty" { + d := &Device{Config: DeviceConfig{Uuid: did}} + if err := pnd.addDevice(d); err != nil { + t.Error(err) + } + } if err := pnd.RemoveDevice(tt.args.uuid); (err != nil) != tt.wantErr { t.Errorf("RemoveDevice() error = %v, wantErr %v", err, tt.wantErr) } - _, ok := pnd.devices[did] - if ok { - t.Errorf("RemoveDevice() device %v still in device store %v", d, pnd.devices) + if pnd.devices.exists(did) && tt.name == "default" { + t.Errorf("RemoveDevice() device still in device store %v", pnd.devices) } }) } @@ -339,25 +445,36 @@ func Test_pndImplementation_RemoveDevice(t *testing.T) { func Test_pndImplementation_RemoveSbi(t *testing.T) { type args struct { - sbiIdentifier string + id uuid.UUID } tests := []struct { name string args args wantErr bool }{ - {name: "default", args: args{sbiIdentifier: "openconfig"}, wantErr: false}, + {name: "default", args: args{id: defaultSbiId}, wantErr: false}, + {name: "fails", args: args{id: uuid.New()}, wantErr: true}, + {name: "fails empty", args: args{id: defaultSbiId}, wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - pnd := freshPnd() - _ = pnd.addSbi(&OpenConfig{}) - if err := pnd.RemoveSbi(tt.args.sbiIdentifier); (err != nil) != tt.wantErr { + pnd := &pndImplementation{ + name: "test-remove-sbi", + description: "test-remove-sbi", + sbic: sbiStore{store{}}, + devices: deviceStore{store{}}, + id: defaultPndId, + } + if tt.name != "fails empty" { + if err := pnd.addSbi(&OpenConfig{id: defaultSbiId}); err != nil { + t.Error(err) + } + } + if err := pnd.RemoveSbi(tt.args.id); (err != nil) != tt.wantErr { t.Errorf("RemoveSbi() error = %v, wantErr %v", err, tt.wantErr) } - sbi, ok := pnd.sbi[tt.args.sbiIdentifier] - if ok { - t.Errorf("RemoveDevice() SBI %v still in SBI store %v", sbi, pnd.sbi) + if pnd.sbic.exists(tt.args.id) { + t.Errorf("RemoveDevice() SBI still in SBI store %v", pnd.sbic) } }) } @@ -396,7 +513,7 @@ func Test_pndImplementation_Request(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { deviceWithMockTransport := mockDevice() - pnd := freshPnd() + pnd := newPnd() tr := deviceWithMockTransport.Transport.(*mocks.Transport) tr.On("Get", mockContext, mock.Anything).Return(mock.Anything, tt.args.rErr) tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr) @@ -404,7 +521,9 @@ func Test_pndImplementation_Request(t *testing.T) { if err := pnd.Request(tt.args.uuid, tt.args.path); (err != nil) != tt.wantErr { t.Errorf("Request() error = %v, wantErr %v", err, tt.wantErr) } - delete(pnd.devices, mdid) + if err := pnd.devices.delete(mdid); err != nil { + t.Error(err) + } }) } } @@ -442,7 +561,7 @@ func Test_pndImplementation_RequestAll(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { deviceWithMockTransport := mockDevice() - pnd := freshPnd() + pnd := newPnd() tr := deviceWithMockTransport.Transport.(*mocks.Transport) tr.On("Get", mockContext, mock.Anything).Return(mock.Anything, tt.args.rErr) tr.On("ProcessResponse", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.rErr) @@ -450,7 +569,9 @@ func Test_pndImplementation_RequestAll(t *testing.T) { if err := pnd.RequestAll(tt.args.path); (err != nil) != tt.wantErr { t.Errorf("RequestAll() error = %v, wantErr %v", err, tt.wantErr) } - delete(pnd.devices, mdid) + if err := pnd.devices.delete(mdid); err != nil { + t.Error(err) + } }) } } diff --git a/nucleus/southbound.go b/nucleus/southbound.go index 7f22d24c8..6d5db29f1 100644 --- a/nucleus/southbound.go +++ b/nucleus/southbound.go @@ -4,6 +4,7 @@ import ( "code.fbi.h-da.de/cocsn/yang-models/generated/arista" "code.fbi.h-da.de/cocsn/yang-models/generated/openconfig" log "github.com/golang/glog" + "github.com/google/uuid" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" "github.com/openconfig/ygot/ytypes" @@ -20,6 +21,7 @@ type SouthboundInterface interface { // Needed for type assertion. SetNode() func(schema *yang.Entry, root interface{}, path *gpb.Path, val interface{}, opts ...ytypes.SetNodeOpt) error Schema() *ytypes.Schema + Id() uuid.UUID } type Tapi struct { @@ -33,6 +35,7 @@ type OpenConfig struct { transport Transport schema *ytypes.Schema + id uuid.UUID } // SbiIdentifier returns the string representation of @@ -62,6 +65,10 @@ func (oc *OpenConfig) SetNode() func(schema *yang.Entry, root interface{}, path } } +func (oc *OpenConfig) Id() uuid.UUID { + return oc.id +} + // deprecated // Use for prototyping only. // Use OpenConfig instead @@ -70,6 +77,11 @@ type AristaOC struct { transport Transport schema *ytypes.Schema + id uuid.UUID +} + +func (oc *AristaOC) Id() uuid.UUID { + return oc.id } func (oc *AristaOC) SbiIdentifier() string { @@ -95,33 +107,3 @@ func (oc *AristaOC) SetNode() func(schema *yang.Entry, root interface{}, path *g return nil } } - -type sbiStorage map[string]SouthboundInterface - -func (s sbiStorage) exists(name string) bool { - _, ok := s[name] - return ok -} - -func (s sbiStorage) add(sbi SouthboundInterface) error { - if s.exists(sbi.SbiIdentifier()) { - return &ErrAlreadyExists{item: sbi} - } - s[sbi.SbiIdentifier()] = sbi - return nil -} - -func (s sbiStorage) Sbi(name string) (SouthboundInterface, error) { - if !s.exists(name) { - return nil, &ErrNotFound{id: name} - } - return s[name], nil -} - -func (s sbiStorage) delete(name string) error { - if !s.exists(name) { - return &ErrNotFound{id: name} - } - delete(s, name) - return nil -} diff --git a/nucleus/store.go b/nucleus/store.go new file mode 100644 index 000000000..5923873d2 --- /dev/null +++ b/nucleus/store.go @@ -0,0 +1,107 @@ +package nucleus + +import ( + "github.com/google/uuid" +) + +type Storable interface { + Id() uuid.UUID +} + +type store map[uuid.UUID]Storable + +func (s store) exists(id uuid.UUID) bool { + _, ok := s[id] + return ok +} + +func (s store) add(item Storable) error { + if s.exists(item.Id()) { + return &ErrAlreadyExists{item: item} + } + s[item.Id()] = item + return nil +} + +func (s store) get(id uuid.UUID) (Storable, error) { + if !s.exists(id) { + return nil, &ErrNotFound{id: id} + } + return s[id], nil +} + +func (s store) delete(id uuid.UUID) error { + if !s.exists(id) { + return &ErrNotFound{id: id} + } + delete(s, id) + return nil +} + +func (s store) UUIDs() []uuid.UUID { + keys := make([]uuid.UUID, len(s)) + + i := 0 + for k := range s { + keys[i] = k + i++ + } + return keys +} + +type sbiStore struct { + store +} + +func (s sbiStore) get(id uuid.UUID) (SouthboundInterface, error) { + item, err := s.store.get(id) + if err != nil { + return nil, err + } + sbi, ok := item.(SouthboundInterface) + if !ok { + return nil, &ErrInvalidTypeAssertion{ + v: sbi, + t: "SouthboundInterface", + } + } + return sbi, nil +} + +type pndStore struct { + store +} + +func (s pndStore) get(id uuid.UUID) (PrincipalNetworkDomain, error) { + item, err := s.store.get(id) + if err != nil { + return nil, err + } + pnd, ok := item.(PrincipalNetworkDomain) + if !ok { + return nil, &ErrInvalidTypeAssertion{ + v: pnd, + t: "PrincipalNetworkDomain", + } + } + return pnd, nil +} + +type deviceStore struct { + store +} + +func (s deviceStore) get(id uuid.UUID) (*Device, error) { + item, err := s.store.get(id) + if err != nil { + return nil, err + } + device, ok := item.(*Device) + if !ok { + return nil, &ErrInvalidTypeAssertion{ + v: device, + t: "Device", + } + } + return device, nil +} diff --git a/nucleus/store_test.go b/nucleus/store_test.go new file mode 100644 index 000000000..8a393efd6 --- /dev/null +++ b/nucleus/store_test.go @@ -0,0 +1,448 @@ +package nucleus + +import ( + "code.fbi.h-da.de/cocsn/gosdn/mocks" + "github.com/google/uuid" + log "github.com/sirupsen/logrus" + "reflect" + "testing" +) + +func testSetupStore() { + var err error + iid, err = uuid.Parse("8495a8ac-a1e8-418e-b787-10f5878b2690") + altIid, err = uuid.Parse("edc5de93-2d15-4586-b2a7-fb1bc770986b") + if err != nil { + log.Fatal(err) + } +} + +var iid uuid.UUID +var altIid uuid.UUID + +func Test_store_add(t *testing.T) { + type args struct { + item Storable + } + tests := []struct { + name string + s store + args args + wantErr bool + }{ + { + name: "default", + s: store{}, + args: args{ + item: &mocks.Storable{}, + }, + }, + { + name: "already exists", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{ + item: &mocks.Storable{}, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.args.item.(*mocks.Storable).On("Id").Return(iid) + switch tt.name { + case "already exixts": + _ = tt.s.add(tt.args.item) + default: + } + if err := tt.s.add(tt.args.item); (err != nil) != tt.wantErr { + t.Errorf("add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func Test_store_delete(t *testing.T) { + type args struct { + id uuid.UUID + } + tests := []struct { + name string + s store + args args + wantErr bool + }{ + { + name: "default", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{id: iid}, + wantErr: false, + }, + { + name: "not found empty", + s: store{}, + args: args{id: iid}, + wantErr: true, + }, + { + name: "not found", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{id: altIid}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.s.delete(tt.args.id); (err != nil) != tt.wantErr { + t.Errorf("delete() error = %v, wantErr %v", err, tt.wantErr) + } + if tt.name == "default" { + item, ok := tt.s[iid] + if ok { + t.Errorf("delete() item %v still in store %v", item, tt.s) + } + } + }) + } +} + +func Test_store_exists(t *testing.T) { + type args struct { + id uuid.UUID + } + tests := []struct { + name string + s store + args args + want bool + }{ + { + name: "default", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{id: iid}, + want: true, + }, + { + name: "not found empty", + s: store{}, + args: args{id: iid}, + want: false, + }, + { + name: "not found", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{id: altIid}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.s.exists(tt.args.id); got != tt.want { + t.Errorf("exists() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_store_get(t *testing.T) { + type args struct { + id uuid.UUID + } + tests := []struct { + name string + s store + args args + want Storable + wantErr bool + }{ + { + name: "exists", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{id: iid}, + want: &mocks.Storable{}, + wantErr: false, + }, + { + name: "not found", + s: store{ + iid: &mocks.Storable{}, + }, + args: args{id: altIid}, + want: nil, + wantErr: true, + }, + { + name: "not found empty", + s: store{}, + args: args{id: iid}, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.s.get(tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("get() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_store_UUIDs(t *testing.T) { + tests := []struct { + name string + s store + want []uuid.UUID + }{ + { + name: "default", + s: store{ + iid: &mocks.Storable{}, + altIid: &mocks.Storable{}, + }, + want: []uuid.UUID{iid, altIid}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.s.UUIDs(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("UUIDs() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_sbiStore_get(t *testing.T) { + type fields struct { + store store + } + type args struct { + id uuid.UUID + } + tests := []struct { + name string + fields fields + args args + want SouthboundInterface + wantErr bool + }{ + { + name: "exists", + fields: fields{ + store: store{ + defaultSbiId: &OpenConfig{id: defaultSbiId}, + }, + }, + args: args{id: defaultSbiId}, + want: &OpenConfig{id: defaultSbiId}, + wantErr: false, + }, + { + name: "fails", + fields: fields{ + store: store{ + defaultSbiId: &OpenConfig{id: defaultSbiId}, + }, + }, + args: args{id: iid}, + wantErr: true, + }, + { + name: "fails empty", + fields: fields{ + store: store{}, + }, + args: args{id: defaultSbiId}, + wantErr: true, + }, + { + name: "fails wrong type", + fields: fields{ + store: store{ + did: &Device{ + Config: DeviceConfig{ + Uuid: did, + }, + }, + }, + }, + args: args{id: did}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := sbiStore{ + store: tt.fields.store, + } + got, err := s.get(tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("get() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_pndStore_get(t *testing.T) { + type fields struct { + store store + } + type args struct { + id uuid.UUID + } + tests := []struct { + name string + fields fields + args args + want PrincipalNetworkDomain + wantErr bool + }{ + { + name: "exists", + fields: fields{ + store: store{ + defaultPndId: &pndImplementation{id: defaultPndId}, + }, + }, + args: args{id: defaultPndId}, + want: &pndImplementation{id: defaultPndId}, + wantErr: false, + }, + { + name: "fails", + fields: fields{ + store: store{ + defaultPndId: &pndImplementation{id: defaultPndId}, + }, + }, + args: args{id: iid}, + wantErr: true, + }, + { + name: "fails empty", + fields: fields{ + store: store{}, + }, + args: args{id: defaultPndId}, + wantErr: true, + }, + { + name: "fails wrong type", + fields: fields{ + store: store{ + did: &Device{ + Config: DeviceConfig{ + Uuid: did, + }, + }, + }, + }, + args: args{id: defaultPndId}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := pndStore{ + store: tt.fields.store, + } + got, err := s.get(tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("get() got = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_deviceStore_get(t *testing.T) { + type fields struct { + store store + } + type args struct { + id uuid.UUID + } + tests := []struct { + name string + fields fields + args args + want *Device + wantErr bool + }{ + { + name: "exists", + fields: fields{ + store: store{ + defaultPndId: &Device{Config: DeviceConfig{Uuid: did}}}}, + args: args{id: defaultPndId}, + want: &Device{ + Config: DeviceConfig{ + Uuid: did, + }, + }, + wantErr: false, + }, + { + name: "fails", + fields: fields{ + store: store{ + defaultPndId: &Device{Config: DeviceConfig{Uuid: did}}}}, + args: args{id: iid}, + wantErr: true, + }, + { + name: "fails empty", + fields: fields{ + store: store{}, + }, + args: args{id: defaultPndId}, + wantErr: true, + }, + { + name: "fails wrong type", + fields: fields{ + store: store{ + defaultPndId: &pndImplementation{id: defaultPndId}}}, + args: args{id: defaultPndId}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := deviceStore{ + store: tt.fields.store, + } + got, err := s.get(tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("get() got = %v, want %v", got, tt.want) + } + }) + } +} -- GitLab