diff --git a/.gitignore b/.gitignore index 6c56d29b92f43a22cd9d291e791c613a303ed15c..5fa2ffa0ffd521c3989ebc87ca0a4b36679083d2 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ controller/api/stores_testing/** controller/northbound/server/stores_testing/** controller/nucleus/stores_testing/** controller/nucleus/**/gostructs.go +controller/rbac/stores_testing/** # stores stores/*.json diff --git a/.gitlab/ci/.test.yml b/.gitlab/ci/.test.yml index 60fea0a976322b297da439ccb425fd3acebd8336..4d033d2ead17f57d1e84c299cca384504122d8a7 100644 --- a/.gitlab/ci/.test.yml +++ b/.gitlab/ci/.test.yml @@ -5,7 +5,7 @@ - when: on_success variables: GOSDN_LOG: "nolog" - GOSDN_CHANGE_TIMEOUT: "100ms" + GOSDN_CHANGE_TIMEOUT: "5000ms" artifacts: when: always reports: diff --git a/cli/cmd/deviceSet.go b/cli/cmd/deviceSet.go index 1c18443b5705897b2e3997a31e92ff64149b9ce8..15806d356c22f43b5719685abe7d30baf2e3e041 100644 --- a/cli/cmd/deviceSet.go +++ b/cli/cmd/deviceSet.go @@ -119,6 +119,6 @@ func fileContentToString(path string) (string, error) { func init() { deviceCmd.AddCommand(deviceSetCmd) deviceSetCmd.Flags().BoolVarP(&replace, "replace", "r", false, "enables replace behaviour") - deviceSetCmd.Flags().StringVar(&file, "json file", "", "reference the path to a file containing your changes as JSON") + deviceSetCmd.Flags().StringVar(&file, "file", "", "reference the path to a file containing your changes as JSON") deviceSetCmd.Flags().BoolVar(&forcePush, "force-push", false, "enables the possibility to instantly push the set without commit/confirm") } diff --git a/controller/interfaces/transport/transport.go b/controller/interfaces/transport/transport.go index a0209d9b7f90f9b7d89a0b19e5bbfeb4158b4044..9d732c370599062776d9151c52fa4395df7c3d6f 100644 --- a/controller/interfaces/transport/transport.go +++ b/controller/interfaces/transport/transport.go @@ -12,7 +12,7 @@ import ( // like RESTCONF or gnmi type Transport interface { Get(ctx context.Context, params ...string) (interface{}, error) - Set(ctx context.Context, payload change.Payload) error + Set(ctx context.Context, payload change.Payload, path string, schema *ytypes.Schema) error Subscribe(ctx context.Context, params ...string) error Type() string ProcessResponse(resp interface{}, root interface{}, models *ytypes.Schema) error diff --git a/controller/mocks/Transport.go b/controller/mocks/Transport.go index ae80536a4f465db7af6e5d47ec8beebd7ee027c4..10fa882e37cf54a9c81a7652ec0da45694e472ef 100644 --- a/controller/mocks/Transport.go +++ b/controller/mocks/Transport.go @@ -63,13 +63,13 @@ func (_m *Transport) ProcessResponse(resp interface{}, root interface{}, models return r0 } -// Set provides a mock function with given fields: ctx, payload -func (_m *Transport) Set(ctx context.Context, payload change.Payload) error { - ret := _m.Called(ctx, payload) +// Set provides a mock function with given fields: ctx, payload, path, schema +func (_m *Transport) Set(ctx context.Context, payload change.Payload, path string, schema *ytypes.Schema) error { + ret := _m.Called(ctx, payload, path, schema) var r0 error - if rf, ok := ret.Get(0).(func(context.Context, change.Payload) error); ok { - r0 = rf(ctx, payload) + if rf, ok := ret.Get(0).(func(context.Context, change.Payload, string, *ytypes.Schema) error); ok { + r0 = rf(ctx, payload, path, schema) } else { r0 = ret.Error(0) } diff --git a/controller/northbound/server/role.go b/controller/northbound/server/role.go index fd742a96e6f21497de4985357d36447fe0090729..4cb05b6e16e885082f2e6a1b88466f000f2a7961 100644 --- a/controller/northbound/server/role.go +++ b/controller/northbound/server/role.go @@ -117,7 +117,6 @@ func (r Role) UpdateRoles(ctx context.Context, request *apb.UpdateRolesRequest) if err != nil { return nil, handleRPCError(labels, err) } - _, err = r.roleService.Get(store.Query{ID: rid}) if err != nil { return nil, status.Errorf(codes.Canceled, "role not found %v", err) diff --git a/controller/nucleus/deviceFilesystemStore.go b/controller/nucleus/deviceFilesystemStore.go index fc57e48cfce24269e906cff34d6f2fb8ac013786..0d9db1f893eacc3d958424b44e1feafead591248 100644 --- a/controller/nucleus/deviceFilesystemStore.go +++ b/controller/nucleus/deviceFilesystemStore.go @@ -8,7 +8,6 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) @@ -23,7 +22,7 @@ type FilesystemDeviceStore struct { // NewFilesystemDeviceStore returns a filesystem implementation for a pnd store. func NewFilesystemDeviceStore(pndUUID uuid.UUID) device.Store { - deviceFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, filesystem.DeviceFilenameSuffix) + deviceFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, store.DeviceFilenameSuffix) store.EnsureFilesystemStorePathExists(deviceFilenameForUUID) return &FilesystemDeviceStore{ diff --git a/controller/nucleus/deviceFilesystemStore_test.go b/controller/nucleus/deviceFilesystemStore_test.go index e3179ad319e491b1911c4fe9035e73cdf36dc542..520a30a520901d75ad4e0cedf3197e97433717f5 100644 --- a/controller/nucleus/deviceFilesystemStore_test.go +++ b/controller/nucleus/deviceFilesystemStore_test.go @@ -9,14 +9,13 @@ import ( spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) func ensureDeviceFilesForTestAreRemoved() { - store.EnsureFilesystemStorePathExists(filesystem.DeviceFilenameSuffix) - wildcartFilename := "*-" + filesystem.DeviceFilenameSuffix + store.EnsureFilesystemStorePathExists(store.DeviceFilenameSuffix) + wildcartFilename := "*-" + store.DeviceFilenameSuffix path := store.GetCompletePathToFileStore(wildcartFilename) files, err := filepath.Glob(path) diff --git a/controller/nucleus/errors/errors.go b/controller/nucleus/errors/errors.go index 7883c35987cb9557c16d6d9af66aceb6617b167d..54be6a61744b799cabc3ee1d7cd79a5c3db7bb4a 100644 --- a/controller/nucleus/errors/errors.go +++ b/controller/nucleus/errors/errors.go @@ -3,6 +3,8 @@ package errors import ( "fmt" "reflect" + + "github.com/openconfig/ygot/ygot" ) // ErrNilClient implements the Error interface and is called if a GNMI Client is nil. @@ -29,7 +31,7 @@ type ErrNotFound struct { } func (e *ErrNotFound) Error() string { - return fmt.Sprintf("ID: %v or Name: %vnot found", e.ID, e.Name) + return fmt.Sprintf("ID: %v or Name: %v not found", e.ID, e.Name) } // ErrAlreadyExists implements the Error interface and is called if a specific ID @@ -72,6 +74,17 @@ func (e ErrUnsupportedPath) Error() string { return fmt.Sprintf("path %v is not supported", e.Path) } +// ErrPathNotFound implements the Error interface and is called if the +// given path is not supported. +type ErrPathNotFound struct { + Path interface{} + Err error +} + +func (e ErrPathNotFound) Error() string { + return fmt.Sprintf("path %v not found: %v", e.Path, e.Err) +} + // ErrNotYetImplemented implements the Error interface and is called if a function // is not implemented yet. type ErrNotYetImplemented struct{} @@ -206,3 +219,15 @@ type ErrCouldNotDelete struct { func (e ErrCouldNotDelete) Error() string { return fmt.Sprintf("could not delete %s", e.StoreName) } + +// ErrNoNewChanges implements the Error interface and is called if a the +// gNMI-Notification created from ygot.Diff does not contain any `updates` or +// `deletes`. +type ErrNoNewChanges struct { + Original ygot.GoStruct + Modified ygot.GoStruct +} + +func (e ErrNoNewChanges) Error() string { + return fmt.Sprintf("There are no changes between %v and %v", e.Original, e.Modified) +} diff --git a/controller/nucleus/gnmi_transport.go b/controller/nucleus/gnmi_transport.go index ff19f96a6afb52f1feeb944980397226d7285910..84b9e3b3d7e06d755de9cb45d733b512b11846d0 100644 --- a/controller/nucleus/gnmi_transport.go +++ b/controller/nucleus/gnmi_transport.go @@ -15,6 +15,7 @@ import ( "code.fbi.h-da.de/danet/gosdn/forks/goarista/gnmi" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/util" "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" log "github.com/sirupsen/logrus" @@ -85,37 +86,109 @@ func (g *Gnmi) Get(ctx context.Context, params ...string) (interface{}, error) { } // Set takes a change.Payload struct. -func (g *Gnmi) Set(ctx context.Context, payload change.Payload) error { +func (g *Gnmi) Set(ctx context.Context, payload change.Payload, path string, schema *ytypes.Schema) error { + p, err := ygot.StringToStructuredPath(path) + if err != nil { + return err + } if g.client == nil { return &errors.ErrNilClient{} } ctx = gnmi.NewContext(ctx, g.config) - return g.applyDiff(ctx, payload) + return g.applyDiff(ctx, payload, p, schema) } -func (g *Gnmi) applyDiff(ctx context.Context, payload change.Payload) error { - op := ctx.Value(types.CtxKeyOperation) +// isGNMINotificationEmpty checks if the given gnmi.Notification does not +// contain any updates or deletes. +func isGNMINotificationEmpty(n *gpb.Notification) bool { + if n.Update == nil || len(n.Update) == 0 { + if n.Delete == nil || len(n.Delete) == 0 { + return true + } + } + return false +} +func (g *Gnmi) applyDiff(ctx context.Context, payload change.Payload, path *gpb.Path, schema *ytypes.Schema) error { diff, err := ygot.Diff(payload.Original, payload.Modified) if err != nil { return err } + + if isGNMINotificationEmpty(diff) { + return errors.ErrNoNewChanges{Original: payload.Original, Modified: payload.Modified} + } + + var json []byte + if op := ctx.Value(types.CtxKeyOperation); op == ppb.ApiOperation_API_OPERATION_UPDATE || op == ppb.ApiOperation_API_OPERATION_REPLACE { + rootCopy, err := ygot.DeepCopy(schema.Root) + if err != nil { + return err + } + + for _, u := range diff.Update { + opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} + if err := g.SetNode(schema.RootSchema(), rootCopy, u.GetPath(), u.GetVal(), opts...); err != nil { + return err + } + } + + ygot.PruneEmptyBranches(rootCopy) + + opts := []ytypes.GetNodeOpt{ + &ytypes.GetHandleWildcards{}, + } + nodes, err := ytypes.GetNode(schema.RootSchema(), rootCopy, path, opts...) + if err != nil { + return err + } + + if len(nodes) == 0 || err != nil || util.IsValueNil(nodes[0].Data) { + return errors.ErrPathNotFound{Path: path, Err: err} + } + + json, err = ygot.Marshal7951(nodes[0].Data, &ygot.RFC7951JSONConfig{AppendModuleName: true}) + if err != nil { + return err + } + } + + req, err := createSetRequest(ctx, diff, json, path) + if err != nil { + return err + } + + resp, err := g.client.Set(ctx, req) + log.Info(resp) + return err +} + +func createSetRequest(ctx context.Context, diff *gpb.Notification, json []byte, path *gpb.Path) (*gpb.SetRequest, error) { + op := ctx.Value(types.CtxKeyOperation) req := &gpb.SetRequest{} if diff.Update != nil { switch op { case ppb.ApiOperation_API_OPERATION_UPDATE: - req.Update = diff.Update + req.Update = []*gpb.Update{{ + Path: path, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: json}, + }, + }} case ppb.ApiOperation_API_OPERATION_REPLACE: - req.Replace = diff.Update + req.Replace = []*gpb.Update{{ + Path: path, + Val: &gpb.TypedValue{ + Value: &gpb.TypedValue_JsonIetfVal{JsonIetfVal: json}, + }, + }} default: - return &errors.ErrOperationNotSupported{Op: op} + return nil, &errors.ErrOperationNotSupported{Op: op} } } else if diff.Delete != nil { req.Delete = diff.Delete } - resp, err := g.client.Set(ctx, req) - log.Info(resp) - return err + return req, nil } //Subscribe subscribes to a gNMI target diff --git a/controller/nucleus/gnmi_transport_test.go b/controller/nucleus/gnmi_transport_test.go index e7715e7fd6c2dd0184ec80de4b9ee71bf493f1e5..deb6742db9cf6255483cbf4fd0b2a627d8910c65 100644 --- a/controller/nucleus/gnmi_transport_test.go +++ b/controller/nucleus/gnmi_transport_test.go @@ -7,9 +7,11 @@ import ( "testing" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/change" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/types" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" + ppb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/pnd" spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" @@ -18,6 +20,7 @@ import ( "code.fbi.h-da.de/danet/gosdn/models/generated/openconfig" gpb "github.com/openconfig/gnmi/proto/gnmi" "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" "github.com/openconfig/ygot/ytypes" "github.com/stretchr/testify/mock" ) @@ -271,11 +274,21 @@ func TestGnmi_ProcessResponse(t *testing.T) { } func TestGnmi_Set(t *testing.T) { + schema, err := openconfig.Schema() + if err != nil { + t.Errorf("Set() error = %v", err) + } + + setResponse := &gpb.SetResponse{} + type fields struct { - transport *Gnmi + transport Gnmi + mockArgumentMatcher any } type args struct { payload change.Payload + path string + ctx context.Context } tests := []struct { name string @@ -285,17 +298,88 @@ func TestGnmi_Set(t *testing.T) { }{ { name: "uninitialised", - fields: fields{&Gnmi{}}, + fields: fields{transport: mockTransport()}, args: args{ payload: change.Payload{}, + path: "/", + ctx: context.WithValue(context.Background(), types.CtxKeyOperation, ppb.ApiOperation_API_OPERATION_UPDATE), // nolint }, wantErr: true, }, - // TODO: Positive test cases + { + name: "updateValue", + fields: fields{ + transport: mockTransport(), + mockArgumentMatcher: mock.MatchedBy(func(input *gpb.SetRequest) bool { + if len(input.Update) == 0 { + return false + } + test, _ := ygot.PathToString(input.Update[0].Path) + return test == "/system/config/hostname" + }), + }, + args: args{ + payload: change.Payload{ + Original: &openconfig.Device{ + System: &openconfig.OpenconfigSystem_System{ + Config: &openconfig.OpenconfigSystem_System_Config{ + Hostname: ygot.String("oldName"), + }, + }, + }, + Modified: &openconfig.Device{ + System: &openconfig.OpenconfigSystem_System{ + Config: &openconfig.OpenconfigSystem_System_Config{ + Hostname: ygot.String("newName"), + }, + }, + }, + }, + path: "/system/config/hostname", + ctx: context.WithValue(context.Background(), types.CtxKeyOperation, ppb.ApiOperation_API_OPERATION_UPDATE), // nolint + }, + wantErr: false, + }, + { + name: "removeValue", + fields: fields{ + transport: mockTransport(), + mockArgumentMatcher: mock.MatchedBy(func(input *gpb.SetRequest) bool { + if len(input.Delete) == 0 { + return false + } + test, _ := ygot.PathToString(input.Delete[0]) + return test == "/system/config/hostname" + }), + }, + args: args{ + payload: change.Payload{ + Original: &openconfig.Device{ + System: &openconfig.OpenconfigSystem_System{ + Config: &openconfig.OpenconfigSystem_System_Config{ + Hostname: ygot.String("oldName"), + }, + }, + }, + Modified: &openconfig.Device{ + System: &openconfig.OpenconfigSystem_System{ + Config: &openconfig.OpenconfigSystem_System_Config{ + Hostname: nil, + }, + }, + }, + }, + path: "/system/config/hostname", + ctx: context.WithValue(context.Background(), types.CtxKeyOperation, ppb.ApiOperation_API_OPERATION_DELETE), // nolint + }, + wantErr: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - err := tt.fields.transport.Set(context.Background(), tt.args.payload) + tt.fields.transport.client.(*mocks.GNMIClient). + On("Set", mockContext, tt.fields.mockArgumentMatcher).Return(setResponse, nil) + err := tt.fields.transport.Set(tt.args.ctx, tt.args.payload, tt.args.path, schema) if (err != nil) != tt.wantErr { t.Errorf("Set() error = %v, wantErr %v", err, tt.wantErr) } diff --git a/controller/nucleus/initialise_test.go b/controller/nucleus/initialise_test.go index ed1d2b16cdaea11009dc59c33a68edf61ec980a8..958991f27435204cc0f977126d6195929f6e4fca 100644 --- a/controller/nucleus/initialise_test.go +++ b/controller/nucleus/initialise_test.go @@ -87,7 +87,7 @@ func targetRunner() { func mockTransport() Gnmi { return Gnmi{ - SetNode: nil, + SetNode: getMockSbi(defaultSbiID).SetNode, RespChan: make(chan *gpb.SubscribeResponse), Options: newGnmiTransportOptions(), client: &mocks.GNMIClient{}, diff --git a/controller/nucleus/pndFilesystemStore.go b/controller/nucleus/pndFilesystemStore.go index 61a42808c0b25112083c1ee0d11e094abd3052aa..beba95994fef0a9cf1c94dca37096efa9987e2d3 100644 --- a/controller/nucleus/pndFilesystemStore.go +++ b/controller/nucleus/pndFilesystemStore.go @@ -9,7 +9,6 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -28,10 +27,10 @@ type FilesystemPndStore struct { // NewFilesystemPndStore returns a filesystem implementation for a pnd store. func NewFilesystemPndStore() FilesystemPndStore { - store.EnsureFilesystemStorePathExists(filesystem.PndFilename) + store.EnsureFilesystemStorePathExists(store.PndFilename) return FilesystemPndStore{ pendingChannels: make(map[uuid.UUID]chan device.Details), - pathToPndFile: store.GetCompletePathToFileStore(filesystem.PndFilename), + pathToPndFile: store.GetCompletePathToFileStore(store.PndFilename), fileMutex: sync.Mutex{}, } } diff --git a/controller/nucleus/pndFilesystemStore_test.go b/controller/nucleus/pndFilesystemStore_test.go index 3480c6a5392c90c7f0ac59e14906c63bcea722fb..29119f9f343710cf91d6500b2393686d65c68734 100644 --- a/controller/nucleus/pndFilesystemStore_test.go +++ b/controller/nucleus/pndFilesystemStore_test.go @@ -6,14 +6,13 @@ import ( "testing" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) func ensurePndFileForTestIsRemoved() { - store.EnsureFilesystemStorePathExists(filesystem.PndFilename) - path := store.GetCompletePathToFileStore(filesystem.PndFilename) + store.EnsureFilesystemStorePathExists(store.PndFilename) + path := store.GetCompletePathToFileStore(store.PndFilename) err := os.Remove(path) if err != nil { diff --git a/controller/nucleus/principalNetworkDomain.go b/controller/nucleus/principalNetworkDomain.go index 3c75a1ae1fdbab46977dc636f72e2e30b8d608ef..ad53c9694b1c85e87f9702cbaed130e2edd278c4 100644 --- a/controller/nucleus/principalNetworkDomain.go +++ b/controller/nucleus/principalNetworkDomain.go @@ -472,7 +472,8 @@ func (pnd *pndImplementation) ChangeOND(duid uuid.UUID, operation ppb.ApiOperati if err != nil { return uuid.Nil, err } - if err := ytypes.SetNode(d.SBI().Schema().RootSchema(), validatedCpy, p, typedValue); err != nil { + opts := []ytypes.SetNodeOpt{&ytypes.InitMissingElements{}, &ytypes.TolerateJSONInconsistencies{}} + if err := ytypes.SetNode(d.SBI().Schema().RootSchema(), validatedCpy, p, typedValue, opts...); err != nil { return uuid.Nil, err } } @@ -488,7 +489,9 @@ func (pnd *pndImplementation) ChangeOND(duid uuid.UUID, operation ppb.ApiOperati callback := func(original ygot.GoStruct, modified ygot.GoStruct) error { ctx := context.WithValue(context.Background(), types.CtxKeyOperation, operation) // nolint payload := change.Payload{Original: original, Modified: modified} - return d.Transport().Set(ctx, payload) + pathToSet := path + schema := d.SBI().Schema() + return d.Transport().Set(ctx, payload, pathToSet, schema) } ch := NewChange(duid, d.GetModel(), validatedCpy, callback) diff --git a/controller/nucleus/principalNetworkDomain_test.go b/controller/nucleus/principalNetworkDomain_test.go index 907e027ab78ee1740d262247d985d76bd11778e5..ac28816d94daad4aaa19d070a72e79fdfb73af71 100644 --- a/controller/nucleus/principalNetworkDomain_test.go +++ b/controller/nucleus/principalNetworkDomain_test.go @@ -959,7 +959,7 @@ func Test_pndImplementation_Confirm(t *testing.T) { d := mockDevice() tr := d.Transport().(*mocks.Transport) - tr.On("Set", mockContext, mock.Anything, mock.Anything).Return(nil) + tr.On("Set", mockContext, mock.Anything, mock.Anything, mock.Anything).Return(nil) _, err := pnd.addDevice(d) if err != nil { t.Error(err) diff --git a/controller/nucleus/sbiFilesystemStore.go b/controller/nucleus/sbiFilesystemStore.go index 52d0f98944ee93b1d8bef02f2f128d3af85f4b6b..64fdeb48104572f8e268c1e53c4a43d3bfb35024 100644 --- a/controller/nucleus/sbiFilesystemStore.go +++ b/controller/nucleus/sbiFilesystemStore.go @@ -7,7 +7,6 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" @@ -23,7 +22,7 @@ type FilesystemSbiStore struct { // NewFilesystemSbiStore returns a filesystem implementation for a pnd store. func NewFilesystemSbiStore(pndUUID uuid.UUID) southbound.Store { - sbiFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, filesystem.SbiFilenameSuffix) + sbiFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, store.SbiFilenameSuffix) store.EnsureFilesystemStorePathExists(sbiFilenameForUUID) return &FilesystemSbiStore{ diff --git a/controller/nucleus/sbiFilesystemStore_test.go b/controller/nucleus/sbiFilesystemStore_test.go index 38ea67d4723ee950e00ba74c05d5b08b286549ae..4ec3de489742d87d2fabef6b02e4ec3fcbb25615 100644 --- a/controller/nucleus/sbiFilesystemStore_test.go +++ b/controller/nucleus/sbiFilesystemStore_test.go @@ -8,14 +8,13 @@ import ( spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) func ensureSbiFilesForTestAreRemoved() { - store.EnsureFilesystemStorePathExists(filesystem.SbiFilenameSuffix) - wildcartFilename := "*-" + filesystem.SbiFilenameSuffix + store.EnsureFilesystemStorePathExists(store.SbiFilenameSuffix) + wildcartFilename := "*-" + store.SbiFilenameSuffix path := store.GetCompletePathToFileStore(wildcartFilename) files, err := filepath.Glob(path) diff --git a/controller/rbac/roleFileSystemStore.go b/controller/rbac/roleFileSystemStore.go new file mode 100644 index 0000000000000000000000000000000000000000..02a2baaeeea0e89aa0021695d9cebec19c6dd985 --- /dev/null +++ b/controller/rbac/roleFileSystemStore.go @@ -0,0 +1,166 @@ +package rbac + +import ( + "encoding/json" + "io/ioutil" + "sync" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" +) + +// FileSystemRoleStore is the filesystem implementation of the role store +type FileSystemRoleStore struct { + fileMutex sync.Mutex + pathToRoleFile string +} + +// NewFileSystemRoleStore returns a filesystem implementation for a role store. +func NewFileSystemRoleStore() rbac.RoleStore { + store.EnsureFilesystemStorePathExists(store.RoleFilename) + return &FileSystemRoleStore{ + fileMutex: sync.Mutex{}, + pathToRoleFile: store.GetCompletePathToFileStore(store.RoleFilename), + } +} + +func (s *FileSystemRoleStore) readAllRolesFromFile() ([]rbac.LoadedRole, error) { + var loadedRoles []rbac.LoadedRole + content, err := ioutil.ReadFile(s.pathToRoleFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(content, &loadedRoles) + if err != nil { + return nil, err + } + + return loadedRoles, nil +} + +func (s *FileSystemRoleStore) writeAllRolesToFile(Roles []rbac.LoadedRole) error { + serializedData, err := json.Marshal(Roles) + if err != nil { + return err + } + + err = ioutil.WriteFile(s.pathToRoleFile, serializedData, 0600) + if err != nil { + return err + } + + return nil +} + +// Add adds a Role to the Role store +func (s *FileSystemRoleStore) Add(RoleToAdd rbac.Role) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + roles, err := s.readAllRolesFromFile() + if err != nil { + return err + } + + var loadedRole rbac.LoadedRole + loadedRole, err = store.TransformObjectToLoadedObject[rbac.Role, rbac.LoadedRole](RoleToAdd) + if err != nil { + return err + } + + roles = append(roles, loadedRole) + + err = s.writeAllRolesToFile(roles) + if err != nil { + return err + } + + return nil +} + +//Delete deletes a Role from the Role store +func (s *FileSystemRoleStore) Delete(RoleToDelete rbac.Role) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + roles, err := s.readAllRolesFromFile() + if err != nil { + return err + } + + for i, role := range roles { + if role.ID == RoleToDelete.ID().String() { + //remove item from slice + roles[i] = roles[len(roles)-1] + roles = roles[:len(roles)-1] + + err = s.writeAllRolesToFile(roles) + if err != nil { + return err + } + + return nil + } + } + + return &errors.ErrNotFound{ID: RoleToDelete.ID} +} + +//Get takes a Roles ID and return the Role if found +func (s *FileSystemRoleStore) Get(query store.Query) (rbac.LoadedRole, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + var role rbac.LoadedRole + roles, err := s.readAllRolesFromFile() + if err != nil { + return role, err + } + + for _, role := range roles { + if role.ID == query.ID.String() || role.RoleName == query.Name { + return role, nil + } + } + + return role, &errors.ErrNotFound{ID: query.ID, Name: query.Name} +} + +// GetAll returns all the Roles +func (s *FileSystemRoleStore) GetAll() ([]rbac.LoadedRole, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + Roles, err := s.readAllRolesFromFile() + return Roles, err +} + +//Update updates an exsisting Role +func (s *FileSystemRoleStore) Update(roleToUpdate rbac.Role) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + loadedRole, err := store.TransformObjectToLoadedObject[rbac.Role, rbac.LoadedRole](roleToUpdate) + if err != nil { + return err + } + Roles, err := s.readAllRolesFromFile() + if err != nil { + return err + } + + for i, Role := range Roles { + if Role.ID == roleToUpdate.ID().String() { + Roles[i] = loadedRole + err = s.writeAllRolesToFile(Roles) + if err != nil { + return err + } + return nil + } + } + + return &errors.ErrNotFound{ID: roleToUpdate.ID().String()} +} diff --git a/controller/rbac/roleFileSystemStore_test.go b/controller/rbac/roleFileSystemStore_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cfc033ee8b3dd537b7b4421c11e73ffa9b79a814 --- /dev/null +++ b/controller/rbac/roleFileSystemStore_test.go @@ -0,0 +1,185 @@ +package rbac + +import ( + "log" + "os" + "path/filepath" + "reflect" + "testing" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" +) + +func ensureRoleFilesForTestAreRemoved() { + store.EnsureFilesystemStorePathExists(store.RoleFilename) + path := store.GetCompletePathToFileStore(store.RoleFilename) + + files, err := filepath.Glob(path) + + if err != nil { + log.Println(err) + } + for _, f := range files { + if err := os.Remove(f); err != nil { + log.Println(err) + } + } +} + +func TestFileSystemRoleStore_Add(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + type args struct { + RoleToAdd rbac.Role + } + var AddRole rbac.Role + tests := []struct { + name string + args args + wantErr bool + }{ + {"AddRole1", args{AddRole}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + if err := s.Add(tt.args.RoleToAdd); (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemRoleStore_Delete(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + type args struct { + RoleToDelete rbac.Role + } + var idtest uuid.UUID + addRole := NewRole(idtest, "testRole", "role", []string{}) + tests := []struct { + name string + args args + wantErr bool + }{ + {"AddRole1", args{ + addRole, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole) + if err := s.Delete(tt.args.RoleToDelete); (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemRoleStore_Get(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + type args struct { + query store.Query + } + var idtest uuid.UUID + var arrTest []string + addRole := NewRole(idtest, "testRole", "role", arrTest) + tests := []struct { + name string + args args + want rbac.LoadedRole + wantErr bool + }{ + {"AddRole1", args{ + store.Query{ + ID: idtest, Name: "test", + }, + }, + rbac.LoadedRole{ID: idtest.String(), RoleName: "testRole", Description: "role", Permissions: arrTest}, + false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole) + got, err := s.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemRoleStore.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemRoleStore_GetAll(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + var idtest uuid.UUID + var arrTest []string + addRole1 := NewRole(idtest, "testRole1", "role1", arrTest) + addRole2 := NewRole(idtest, "testRole2", "role2", arrTest) + addRole3 := NewRole(idtest, "testRole3", "role3", arrTest) + tests := []struct { + name string + want []rbac.LoadedRole + wantErr bool + }{ + { + "testRole", + []rbac.LoadedRole{{ID: idtest.String(), RoleName: "testRole1", Description: "role1", Permissions: arrTest}, {ID: idtest.String(), RoleName: "testRole2", Description: "role2", Permissions: arrTest}, {ID: idtest.String(), RoleName: "testRole3", Description: "role3", Permissions: arrTest}}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole1) + s.Add(addRole2) + s.Add(addRole3) + got, err := s.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemRoleStore.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemRoleStore_Update(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + var idtest uuid.UUID + var arrTest []string + addRole1 := NewRole(idtest, "testRole1", "role1", arrTest) + type args struct { + roleToUpdate rbac.Role + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"AddRole1", args{ + addRole1, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole1) + if err := s.Update(tt.args.roleToUpdate); (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Update() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/controller/rbac/roleStore.go b/controller/rbac/roleStore.go index 4482168fa331fbe70fbee6830df0283d5e49a194..35b9a51276641d34745acf5613f35191580a27cb 100644 --- a/controller/rbac/roleStore.go +++ b/controller/rbac/roleStore.go @@ -19,13 +19,10 @@ func NewRoleStore() rbac.RoleStore { storeMode := store.GetStoreMode() switch storeMode { - case store.Filesystem: - return NewMemoryRoleStore() case store.Database: return &DatabaseRoleStore{"role.json"} - case store.Memory: - return NewMemoryRoleStore() default: - return nil + store := NewFileSystemRoleStore() + return store } } diff --git a/controller/rbac/userFileSystemStore.go b/controller/rbac/userFileSystemStore.go new file mode 100644 index 0000000000000000000000000000000000000000..11f2184c78bd233fd5cde3c0d42e710e8d2c7fa0 --- /dev/null +++ b/controller/rbac/userFileSystemStore.go @@ -0,0 +1,166 @@ +package rbac + +import ( + "encoding/json" + "io/ioutil" + "sync" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" +) + +// FileSystemUserStore is the filesystem implementation of the user store +type FileSystemUserStore struct { + fileMutex sync.Mutex + pathToUserFile string +} + +// NewFileSystemUserStore returns a filesystem implementation for a user store. +func NewFileSystemUserStore() rbac.UserStore { + store.EnsureFilesystemStorePathExists(store.UserFilename) + return &FileSystemUserStore{ + fileMutex: sync.Mutex{}, + pathToUserFile: store.GetCompletePathToFileStore(store.UserFilename), + } +} + +func (s *FileSystemUserStore) readAllUsersFromFile() ([]rbac.LoadedUser, error) { + var loadedUsers []rbac.LoadedUser + content, err := ioutil.ReadFile(s.pathToUserFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(content, &loadedUsers) + if err != nil { + return nil, err + } + + return loadedUsers, nil +} + +func (s *FileSystemUserStore) writeAllUsersToFile(users []rbac.LoadedUser) error { + serializedData, err := json.Marshal(users) + if err != nil { + return err + } + + err = ioutil.WriteFile(s.pathToUserFile, serializedData, 0600) + if err != nil { + return err + } + + return nil +} + +// Add adds a User to the User store +func (s *FileSystemUserStore) Add(UserToAdd rbac.User) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + users, err := s.readAllUsersFromFile() + if err != nil { + return err + } + + var loadedUser rbac.LoadedUser + loadedUser, err = store.TransformObjectToLoadedObject[rbac.User, rbac.LoadedUser](UserToAdd) + if err != nil { + return err + } + + users = append(users, loadedUser) + + err = s.writeAllUsersToFile(users) + if err != nil { + return err + } + + return nil +} + +//Delete deletes a User from the User store +func (s *FileSystemUserStore) Delete(userToDelete rbac.User) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + users, err := s.readAllUsersFromFile() + if err != nil { + return err + } + + for i, user := range users { + if user.ID == userToDelete.ID().String() { + //remove item from slice + users[i] = users[len(users)-1] + users = users[:len(users)-1] + + err = s.writeAllUsersToFile(users) + if err != nil { + return err + } + + return nil + } + } + + return &errors.ErrNotFound{ID: userToDelete.ID} +} + +//Get takes a Users ID and return the User if found +func (s *FileSystemUserStore) Get(query store.Query) (rbac.LoadedUser, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + var user rbac.LoadedUser + + users, err := s.readAllUsersFromFile() + if err != nil { + return user, err + } + + for _, user := range users { + if user.ID == query.ID.String() || user.UserName == query.Name { + return user, nil + } + } + return user, &errors.ErrNotFound{ID: query.ID, Name: query.Name} +} + +// GetAll returns all the Users +func (s *FileSystemUserStore) GetAll() ([]rbac.LoadedUser, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + Users, err := s.readAllUsersFromFile() + return Users, err +} + +//Update updates an exsisting user +func (s *FileSystemUserStore) Update(userToUpdate rbac.User) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + loadedUser, err := store.TransformObjectToLoadedObject[rbac.User, rbac.LoadedUser](userToUpdate) + if err != nil { + return err + } + users, err := s.readAllUsersFromFile() + if err != nil { + return err + } + + for i, user := range users { + if user.ID == userToUpdate.ID().String() { + users[i] = loadedUser + err = s.writeAllUsersToFile(users) + if err != nil { + return err + } + return nil + } + } + + return &errors.ErrNotFound{ID: userToUpdate.ID().String()} +} diff --git a/controller/rbac/userFileSystemStore_test.go b/controller/rbac/userFileSystemStore_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4261e982ec15079bfee2240858fba617e724a21e --- /dev/null +++ b/controller/rbac/userFileSystemStore_test.go @@ -0,0 +1,201 @@ +package rbac + +import ( + "log" + "os" + "path/filepath" + "reflect" + "testing" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" +) + +func ensureUserFilesForTestAreRemoved() { + store.EnsureFilesystemStorePathExists(store.UserFilename) + path := store.GetCompletePathToFileStore(store.UserFilename) + + files, err := filepath.Glob(path) + + if err != nil { + log.Println(err) + } + for _, f := range files { + if err := os.Remove(f); err != nil { + log.Println(err) + } + } +} + +func TestFileSystemUserStore_Add(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + UserToAdd rbac.User + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "testUser", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + wantErr bool + }{ + { + "testUser", + args{testingUser}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + + if err := s.Add(tt.args.UserToAdd); (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemUserStore_Delete(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + UserToDelete rbac.User + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + wantErr bool + }{ + { + "testUser", + args{testingUser}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser) + if err := s.Delete(tt.args.UserToDelete); (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemUserStore_Get(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + query store.Query + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + want rbac.LoadedUser + wantErr bool + }{ + { + "testUser", + args{ + store.Query{ID: idtest, Name: "test"}, + }, + rbac.LoadedUser{ID: idtest.String(), UserName: "", Roles: role, Password: "xyz", Token: "svsvsfbdwbwbev", Salt: "svswvasfbw"}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser) + got, err := s.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemUserStore.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemUserStore_GetAll(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + var idtest uuid.UUID + var role map[string]string + testingUser1 := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbevasf", "svswvasfbwasv") + testingUser2 := NewUser(idtest, "", role, "abc", "svsvsfbdwbwbevsav", "svswvasfbwadf") + testingUser3 := NewUser(idtest, "", role, "lmn", "svsvsfbdwbwbevscv", "svswvasfbwasd") + tests := []struct { + name string + want []rbac.LoadedUser + wantErr bool + }{ + { + "testUser", + []rbac.LoadedUser{{ID: idtest.String(), UserName: "", Roles: role, Password: "xyz", Token: "svsvsfbdwbwbevasf", Salt: "svswvasfbwasv"}, {ID: idtest.String(), UserName: "", Roles: role, Password: "abc", Token: "svsvsfbdwbwbevsav", Salt: "svswvasfbwadf"}, {ID: idtest.String(), UserName: "", Roles: role, Password: "lmn", Token: "svsvsfbdwbwbevscv", Salt: "svswvasfbwasd"}}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser1) + s.Add(testingUser2) + s.Add(testingUser3) + got, err := s.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemUserStore.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemUserStore_Update(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + userToUpdate rbac.User + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + wantErr bool + }{ + { + "testUser", + args{ + testingUser, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser) + if err := s.Update(tt.args.userToUpdate); (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Update() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/controller/rbac/userStore.go b/controller/rbac/userStore.go index 1524abe9c14f286b8fa641e5b3ab8a5ad1df003d..e157d8bf0db359df1e839e4b71529b34ed8aca2c 100644 --- a/controller/rbac/userStore.go +++ b/controller/rbac/userStore.go @@ -19,13 +19,10 @@ func NewUserStore() rbac.UserStore { storeMode := store.GetStoreMode() switch storeMode { - case store.Filesystem: - return NewMemoryUserStore() case store.Database: return &DatabaseUserStore{"user.json"} - case store.Memory: - return NewMemoryUserStore() default: - return nil + store := NewFileSystemUserStore() + return store } } diff --git a/controller/nucleus/filesystem/filesystem-settings.go b/controller/store/filesystem-settings.go similarity index 61% rename from controller/nucleus/filesystem/filesystem-settings.go rename to controller/store/filesystem-settings.go index 8df6e31e7ea541fa4484c7c83c125bfe8925682d..5a23dd0b9f54d556b795ec0c5ec6a7cac248980f 100644 --- a/controller/nucleus/filesystem/filesystem-settings.go +++ b/controller/store/filesystem-settings.go @@ -1,4 +1,4 @@ -package filesystem +package store const ( // PndFilename is the name of the file where the pnds are stored @@ -7,4 +7,8 @@ const ( DeviceFilenameSuffix string = "deviceStore.json" // SbiFilenameSuffix is the suffix of the file where the sbis are stored SbiFilenameSuffix string = "sbiStore.json" + // UserFilename is the name of the file where the users are stored + UserFilename string = "userStore.json" + // RoleFilename is the name of the file where the roles are stored + RoleFilename string = "roleStore.json" ) diff --git a/controller/test/integration/nucleusIntegration_test.go b/controller/test/integration/nucleusIntegration_test.go index 333efb1778f6acf0a22cc9f54f9ed57e82d150bc..b85058a46a6c8b4a1d691bb8633db8f63aff2f94 100644 --- a/controller/test/integration/nucleusIntegration_test.go +++ b/controller/test/integration/nucleusIntegration_test.go @@ -14,6 +14,7 @@ import ( spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/change" + "code.fbi.h-da.de/danet/gosdn/models/generated/openconfig" "code.fbi.h-da.de/danet/gosdn/controller/nucleus" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" @@ -92,6 +93,11 @@ func testSetupIntegration() { } func TestGnmi_SetInvalidIntegration(t *testing.T) { + schema, err := openconfig.Schema() + if err != nil { + t.Errorf("Set ") + + } if testing.Short() { t.Skip("skipping integration test") } @@ -101,6 +107,7 @@ func TestGnmi_SetInvalidIntegration(t *testing.T) { type args struct { ctx context.Context payload change.Payload + path string } tests := []struct { name string @@ -120,6 +127,7 @@ func TestGnmi_SetInvalidIntegration(t *testing.T) { args: args{ ctx: context.Background(), payload: change.Payload{}, + path: "/", }, wantErr: true, }, @@ -129,6 +137,7 @@ func TestGnmi_SetInvalidIntegration(t *testing.T) { args: args{ ctx: context.Background(), payload: change.Payload{}, + path: "/", }, wantErr: true, }, @@ -145,7 +154,7 @@ func TestGnmi_SetInvalidIntegration(t *testing.T) { t.Errorf("SetInvalidIntegration() error = %v, wantErr %v", err, tt.wantErr) return } - err = g.Set(tt.args.ctx, tt.args.payload) + err = g.Set(tt.args.ctx, tt.args.payload, tt.args.path, schema) if (err != nil) != tt.wantErr { t.Errorf("SetInvalidIntegration() error = %v, wantErr %v", err, tt.wantErr) return