Skip to content

Commit

Permalink
feat(db): add repository package for persistence
Browse files Browse the repository at this point in the history
This is not full implementation yet, just the start

Signed-off-by: Boris Glimcher <Boris.Glimcher@emc.com>
  • Loading branch information
glimchb committed Sep 11, 2023
1 parent 6b042bb commit 8233dbe
Show file tree
Hide file tree
Showing 11 changed files with 406 additions and 0 deletions.
12 changes: 12 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

pe "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go"
"github.com/opiproject/opi-evpn-bridge/pkg/evpn"
"github.com/opiproject/opi-evpn-bridge/pkg/repository"

"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
Expand All @@ -28,6 +29,17 @@ func main() {
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
// Provision for persistent storage here
dbvrf, err := repository.VrfFactory("memory")
if err != nil {
log.Fatalf("failed to create database: %v", err)
}
dbvsvi, err := repository.SviFactory("memory")
if err != nil {
log.Fatalf("failed to create database: %v", err)
}
log.Printf("todo: use db in opi server (%v) and (%v)", dbvrf, dbvsvi)

s := grpc.NewServer()
opi := evpn.NewServer()

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ require (
)

require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/go-redis/redis v6.15.9+incompatible // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/redis/go-redis/v9 v9.1.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
golang.org/x/net v0.14.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
Expand All @@ -18,6 +24,8 @@ github.com/opiproject/opi-api v0.0.0-20230905130004-eac732ac240b h1:jAEYUnARydKp
github.com/opiproject/opi-api v0.0.0-20230905130004-eac732ac240b/go.mod h1:92pv4ulvvPMuxCJ9ND3aYbmBfEMLx0VCjpkiR7ZTqPY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
Expand Down
37 changes: 37 additions & 0 deletions pkg/repository/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

// OperationError when cannot perform a given operation on database (SET, GET or DELETE)
type OperationError struct {
operation string
}

func (err *OperationError) Error() string {
return "Could not perform the " + err.operation + " operation."
}

// DownError when its not a redis.Nil response, in this case the database is down
type DownError struct{}

func (dbe *DownError) Error() string {
return "Database is down"
}

// CreateDatabaseError when cannot perform set on database
type CreateDatabaseError struct{}

func (err *CreateDatabaseError) Error() string {
return "Could not create Databse"
}

// NotImplementedDatabaseError when user tries to create a not implemented database
type NotImplementedDatabaseError struct {
database string
}

func (err *NotImplementedDatabaseError) Error() string {
return err.database + " not implemented"
}
47 changes: 47 additions & 0 deletions pkg/repository/memory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

import (
"fmt"
"sync"
)

type memoryDatabase struct {
data map[string]string
lock sync.RWMutex
}

func newMemoryDatabase() *memoryDatabase {
return &memoryDatabase{
data: make(map[string]string),
// lock: &sync.RWMutex{},
}
}

func (repo *memoryDatabase) Set(key string, value string) (string, error) {
repo.lock.RLock()
defer repo.lock.RUnlock()
repo.data[key] = value
return key, nil
}

func (repo *memoryDatabase) Get(key string) (string, error) {
repo.lock.RLock()
defer repo.lock.RUnlock()
value, ok := repo.data[key]
if !ok {
// TODO: use our own errors, maybe OperationError ?
return "", fmt.Errorf("value does not exist for key: %s", key)
}
return value, nil
}

func (repo *memoryDatabase) Delete(key string) (string, error) {
repo.lock.RLock()
defer repo.lock.RUnlock()
delete(repo.data, key)
return key, nil
}
38 changes: 38 additions & 0 deletions pkg/repository/models_svi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

import (
"encoding/binary"
"net"

pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go"
)

// Svi object, separate from protobuf for decoupling
type Svi struct {
VrfRefKey string
LogicalBridgeRefKey string
MacAddress net.HardwareAddr
GwIP []net.IPNet
}

// NewSvi creates new SVI object from protobuf message
func NewSvi(in *pb.Svi) *Svi {
mac := net.HardwareAddr(in.Spec.MacAddress)
gwIPList := []net.IPNet{}
for _, item := range in.Spec.GwIpPrefix {
myip := make(net.IP, 4)
binary.BigEndian.PutUint32(myip, item.Addr.GetV4Addr())
gip := net.IPNet{IP: myip, Mask: net.CIDRMask(int(item.Len), 32)}
gwIPList = append(gwIPList, gip)
}
return &Svi{VrfRefKey: in.Spec.Vrf, LogicalBridgeRefKey: in.Spec.LogicalBridge, MacAddress: mac, GwIP: gwIPList}
}

// ToPb transforms SVI object to protobuf message
func (in *Svi) ToPb() (*pb.Svi, error) {
return &pb.Svi{Spec: nil, Status: nil}, nil
}
35 changes: 35 additions & 0 deletions pkg/repository/models_vrf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

import (
"encoding/binary"
"net"

pb "github.com/opiproject/opi-api/network/evpn-gw/v1alpha1/gen/go"
)

// Vrf object, separate from protobuf for decoupling
type Vrf struct {
Vni uint32
LoopbackIP net.IPNet
VtepIP net.IPNet
}

// NewVrf creates new VRF object from protobuf message
func NewVrf(in *pb.Vrf) *Vrf {
loopip := make(net.IP, 4)
binary.BigEndian.PutUint32(loopip, in.Spec.LoopbackIpPrefix.Addr.GetV4Addr())
lip := net.IPNet{IP: loopip, Mask: net.CIDRMask(int(in.Spec.LoopbackIpPrefix.Len), 32)}
vtepip := make(net.IP, 4)
binary.BigEndian.PutUint32(vtepip, in.Spec.VtepIpPrefix.Addr.GetV4Addr())
vip := net.IPNet{IP: vtepip, Mask: net.CIDRMask(int(in.Spec.VtepIpPrefix.Len), 32)}
return &Vrf{Vni: *in.Spec.Vni, LoopbackIP: lip, VtepIP: vip}
}

// ToPb transforms VRF object to protobuf message
func (in *Vrf) ToPb() (*pb.Vrf, error) {
return &pb.Vrf{Spec: nil, Status: nil}, nil
}
57 changes: 57 additions & 0 deletions pkg/repository/redis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

import "github.com/go-redis/redis"

type redisDatabase struct {
client *redis.Client
}

func newRedisDatabase() (*redisDatabase, error) {
// TODO: pass address
url := "redis://redis:6379?password=&protocol=3/vrf"
opts, err := redis.ParseURL(url)
if err != nil {
return nil, &CreateDatabaseError{}
}
client := redis.NewClient(opts)
_, err = client.Ping().Result() // makes sure database is connected
if err != nil {
return nil, &CreateDatabaseError{}
}
return &redisDatabase{client: client}, nil
}

func (r *redisDatabase) Set(key string, value string) (string, error) {
_, err := r.client.Set(key, value, 0).Result()
if err != nil {
return generateError("set", err)
}
return key, nil
}

func (r *redisDatabase) Get(key string) (string, error) {
value, err := r.client.Get(key).Result()
if err != nil {
return generateError("get", err)
}
return value, nil
}

func (r *redisDatabase) Delete(key string) (string, error) {
_, err := r.client.Del(key).Result()
if err != nil {
return generateError("delete", err)
}
return key, nil
}

func generateError(operation string, err error) (string, error) {
if err == redis.Nil {
return "", &OperationError{operation}
}
return "", &DownError{}
}
24 changes: 24 additions & 0 deletions pkg/repository/repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

// IKeyValueStore abstraction
type IKeyValueStore interface {
Set(key string, value string) (string, error)
Get(key string) (string, error)
Delete(key string) (string, error)
}

// Factory pattern to create new IKeyValueStore
func Factory(databaseImplementation string) (IKeyValueStore, error) {
switch databaseImplementation {
case "redis":
return newRedisDatabase()
case "memory":
return newMemoryDatabase(), nil
default:
return nil, &NotImplementedDatabaseError{databaseImplementation}
}
}
72 changes: 72 additions & 0 deletions pkg/repository/repository_svi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2022-2023 Dell Inc, or its subsidiaries.

// Package repository is the database abstraction implementing repository design pattern
package repository

import (
"encoding/json"
)

// ISviRepository abstraction
type ISviRepository interface {
SetSvi(resourceName string, svi *Svi) error
GetSvi(resourceName string) (*Svi, error)
DeleteSvi(resourceName string) error
}

// Factory pattern to create new ISviRepository

Check warning on line 18 in pkg/repository/repository_svi.go

View workflow job for this annotation

GitHub Actions / call / golangci

exported: comment on exported function SviFactory should be of the form "SviFactory ..." (revive)
func SviFactory(databaseImplementation string) (ISviRepository, error) {
kvstore, err := Factory(databaseImplementation)
if err != nil {
// TODO: use our own errors, maybe OperationError ?
return nil, err
}
return newSviDatabase(kvstore), nil
}

// sviDatabase implements ISviRepository interface
type sviDatabase struct {
kvstore IKeyValueStore
}

func newSviDatabase(kvstore IKeyValueStore) *sviDatabase {
return &sviDatabase{
kvstore: kvstore,
}
}

func (repo *sviDatabase) GetSvi(resourceName string) (*Svi, error) {
value, err := repo.kvstore.Get(resourceName)
if err != nil {
// TODO: use our own errors, maybe OperationError ?
return nil, err
}
svi := &Svi{}
err = json.Unmarshal([]byte(value), svi)
if err != nil {
// TODO: use our own errors, maybe OperationError ?
return nil, err
}
return svi, nil
}

func (repo *sviDatabase) SetSvi(resourceName string, svi *Svi) error {
value, err := json.Marshal(svi)
if err != nil {
return err
}
_, err = repo.kvstore.Set(resourceName, string(value))
if err != nil {
return err
}
return nil
}

func (repo *sviDatabase) DeleteSvi(resourceName string) error {
_, err := repo.kvstore.Delete(resourceName)
if err != nil {
return err
}
return nil
}
Loading

0 comments on commit 8233dbe

Please sign in to comment.