add initial files
This commit is contained in:
commit
6233590bb4
8 changed files with 307 additions and 0 deletions
11
go.mod
Normal file
11
go.mod
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module github.com/davidallendj/partitions
|
||||
|
||||
go 1.23.3
|
||||
|
||||
require github.com/rs/zerolog v1.33.0
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
golang.org/x/sys v0.12.0 // indirect
|
||||
)
|
||||
15
go.sum
Normal file
15
go.sum
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
39
internal/groups/groups.go
Normal file
39
internal/groups/groups.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package groups
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/davidallendj/partitions/internal/partitions"
|
||||
)
|
||||
|
||||
type Group struct {
|
||||
Name string
|
||||
Labels []string
|
||||
}
|
||||
|
||||
func (g *Group) GetNodeIDs(pm *partitions.DefaultManager) []string {
|
||||
foundNodes := []string{}
|
||||
for _, label := range g.Labels {
|
||||
nodeID := pm.LookupMember(label)
|
||||
if nodeID != nil {
|
||||
// check and make sure we're not duplicating node IDs
|
||||
if !slices.Contains(foundNodes, *nodeID) {
|
||||
foundNodes = append(foundNodes, *nodeID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundNodes
|
||||
}
|
||||
|
||||
func (g *Group) GetPartitions(pm *partitions.DefaultManager) []string {
|
||||
foundPartitions := []string{}
|
||||
for _, label := range g.Labels {
|
||||
partition := pm.LookupPartitionByMemberID(label)
|
||||
if partition != nil {
|
||||
if !slices.Contains(foundPartitions, partition.ID) {
|
||||
foundPartitions = append(foundPartitions, partition.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundPartitions
|
||||
}
|
||||
11
internal/nodes/nodes.go
Normal file
11
internal/nodes/nodes.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package nodes
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrExists = errors.New("node exists")
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
ID string
|
||||
}
|
||||
149
internal/partitions/manager.go
Normal file
149
internal/partitions/manager.go
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
package partitions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/davidallendj/partitions/internal/nodes"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrExists = errors.New("partition exists")
|
||||
ErrNotFound = errors.New("partition not found")
|
||||
)
|
||||
|
||||
type Manager[T comparable] struct {
|
||||
partitions []partition[T]
|
||||
}
|
||||
|
||||
func (m *Manager[T]) CreatePartition(id string, members []T) error {
|
||||
// todo: deduplicate node ids
|
||||
|
||||
// check if the partition already exists and fail if it does
|
||||
if m.ContainsPartition(id) {
|
||||
return ErrExists
|
||||
}
|
||||
|
||||
// check the manager to see if there a nodes being added to multiple partitions
|
||||
for _, member := range members {
|
||||
if m.ContainsMember(member) {
|
||||
return fmt.Errorf("%v: %v", nodes.ErrExists, member)
|
||||
}
|
||||
}
|
||||
|
||||
// no other issues at this point, so add the partition with nodes
|
||||
m.partitions = append(m.partitions, partition[T]{ID: id, Members: members})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager[T]) AddNodeToPartition(partitionID string, member T) error {
|
||||
// do lookup to find existing partition
|
||||
partition := m.LookupPartitionByID(partitionID)
|
||||
if partition != nil {
|
||||
// check if node already exists and fail if it does
|
||||
if m.ContainsMember(member) {
|
||||
return fmt.Errorf("%v: %v", nodes.ErrExists, member)
|
||||
}
|
||||
} else {
|
||||
// no partition found, so return error
|
||||
return ErrNotFound
|
||||
}
|
||||
// add member to partition and update manager
|
||||
partition.Members = append(partition.Members, member)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager[T]) LookupPartitionByID(id string) *partition[T] {
|
||||
// try and get index of partition with ID
|
||||
index := slices.IndexFunc(m.partitions, func(p partition[T]) bool {
|
||||
return p.ID == id
|
||||
})
|
||||
// found a partition, so return it
|
||||
if index >= 0 {
|
||||
return &m.partitions[index]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager[T]) LookupPartitionByMemberID(member T) *partition[T] {
|
||||
partitionIndex, _ := m.lookupMember(member)
|
||||
if partitionIndex >= 0 {
|
||||
return &m.partitions[partitionIndex]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager[T]) LookupMember(member T) *T {
|
||||
var (
|
||||
partitionIndex int
|
||||
memberIndex int
|
||||
)
|
||||
partitionIndex, memberIndex = m.lookupMember(member)
|
||||
return m.getNodeFromPartition(partitionIndex, memberIndex)
|
||||
}
|
||||
|
||||
func (m *Manager[T]) ContainsPartition(id string) bool {
|
||||
return m.LookupPartitionByID(id) != nil
|
||||
}
|
||||
|
||||
func (m *Manager[T]) ContainsMember(member T) bool {
|
||||
var (
|
||||
partitionIndex int
|
||||
memberIndex int
|
||||
)
|
||||
|
||||
partitionIndex, memberIndex = m.lookupMember(member)
|
||||
return partitionIndex >= 0 && memberIndex >= 0
|
||||
}
|
||||
|
||||
func (m *Manager[T]) GetPartitions() []partition[T] {
|
||||
return m.partitions
|
||||
}
|
||||
|
||||
func (m *Manager[T]) GetPartitionIDs() []string {
|
||||
partitionIDs := []string{}
|
||||
for _, partition := range m.partitions {
|
||||
partitionIDs = append(partitionIDs, partition.ID)
|
||||
}
|
||||
return partitionIDs
|
||||
}
|
||||
|
||||
func (m *Manager[T]) GetPartitionMembers() []T {
|
||||
members := []T{}
|
||||
for _, partition := range m.partitions {
|
||||
members = append(members, partition.Members...)
|
||||
}
|
||||
return members
|
||||
}
|
||||
|
||||
func (m *Manager[T]) getNodeFromPartition(partitionIndex int, memberIndex int) *T {
|
||||
if partitionIndex >= 0 && memberIndex >= 0 {
|
||||
return &m.partitions[partitionIndex].Members[memberIndex]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manager[T]) lookupMember(member T) (int, int) {
|
||||
var (
|
||||
partitionIndex int
|
||||
memberIndex int
|
||||
)
|
||||
|
||||
// check all partitions for nodes
|
||||
for _, partition := range m.partitions {
|
||||
memberIndex = slices.IndexFunc(partition.Members, func(testMember T) bool {
|
||||
return member == testMember
|
||||
})
|
||||
// we found the node in the partition so return
|
||||
if memberIndex >= 0 {
|
||||
return partitionIndex, memberIndex
|
||||
}
|
||||
partitionIndex += 1
|
||||
}
|
||||
// return negative values to indicate the node was not found
|
||||
return -1, -1
|
||||
}
|
||||
|
||||
type NodeManager = Manager[nodes.Node]
|
||||
type DefaultManager = Manager[string]
|
||||
11
internal/partitions/partitions.go
Normal file
11
internal/partitions/partitions.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package partitions
|
||||
|
||||
import "github.com/davidallendj/partitions/internal/nodes"
|
||||
|
||||
type partition[T comparable] struct {
|
||||
ID string
|
||||
Members []T
|
||||
}
|
||||
|
||||
type nodePartition = partition[nodes.Node]
|
||||
type defaultPartition = partition[string]
|
||||
71
main.go
Normal file
71
main.go
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/davidallendj/partitions/internal/groups"
|
||||
"github.com/davidallendj/partitions/internal/partitions"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
pm = partitions.DefaultManager{}
|
||||
n1 = "nid0001"
|
||||
n2 = "nid0002"
|
||||
n3 = "nid0003"
|
||||
p1 = "test1"
|
||||
p2 = "test2"
|
||||
)
|
||||
|
||||
// create new partitions with partition manager while testing adding
|
||||
// the same partition multiple times
|
||||
unwrapError(pm.CreatePartition(p1, []string{n3}))
|
||||
unwrapError(pm.CreatePartition(p2, nil))
|
||||
unwrapError(pm.CreatePartition(p2, nil))
|
||||
|
||||
// try and put the same node in multiple partitions which should cause error
|
||||
unwrapError(pm.AddNodeToPartition(p1, n1))
|
||||
unwrapError(pm.AddNodeToPartition(p1, n1))
|
||||
unwrapError(pm.AddNodeToPartition(p2, n2))
|
||||
|
||||
// try and put the same node in multiple groups
|
||||
var (
|
||||
g1 = groups.Group{
|
||||
Name: "group1",
|
||||
Labels: []string{n1, n2, "hello"},
|
||||
}
|
||||
g2 = groups.Group{
|
||||
Name: "group2",
|
||||
Labels: []string{n1, n3, "world"},
|
||||
}
|
||||
)
|
||||
|
||||
g1NodeIDs := ToJSON(g1.GetNodeIDs(&pm))
|
||||
g1PartitionIDs := ToJSON(g1.GetPartitions(&pm))
|
||||
g2NodeIDs := ToJSON(g2.GetNodeIDs(&pm))
|
||||
g2PartitionIDs := ToJSON(g2.GetPartitions(&pm))
|
||||
|
||||
log.Info().Any("manager.partitions", pm.GetPartitions()).Msg("partition manager")
|
||||
log.Info().
|
||||
Any("group", g1).
|
||||
Any("found node IDs in manager", g1NodeIDs).
|
||||
Any("partitions containing found nodes", g1PartitionIDs).
|
||||
Msg("group 1")
|
||||
log.Info().
|
||||
Any("group", g2).
|
||||
Any("found node IDs in manager", g2NodeIDs).
|
||||
Any("partitions containing found nodes", g2PartitionIDs).
|
||||
Msg("group 2")
|
||||
}
|
||||
|
||||
func unwrapError(err error) {
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("something went wrong...")
|
||||
}
|
||||
}
|
||||
|
||||
func ToJSON(v any) string {
|
||||
b, _ := json.Marshal(v)
|
||||
return string(b)
|
||||
}
|
||||
BIN
partitions
Executable file
BIN
partitions
Executable file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue