add initial files

This commit is contained in:
David Allen 2024-11-17 13:47:37 -07:00
commit 6233590bb4
Signed by: towk
GPG key ID: 793B2924A49B3A3F
8 changed files with 307 additions and 0 deletions

11
go.mod Normal file
View 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
View 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
View 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
View file

@ -0,0 +1,11 @@
package nodes
import "errors"
var (
ErrExists = errors.New("node exists")
)
type Node struct {
ID string
}

View 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]

View 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
View 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

Binary file not shown.