-
Notifications
You must be signed in to change notification settings - Fork 111
/
Copy pathapi_resource_collection.go
150 lines (131 loc) · 3.69 KB
/
api_resource_collection.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package resource
import (
"strings"
"sync"
"github.com/pkg/errors"
)
// APIResourceCollection defines a collection of typed resources.
type APIResourceCollection[T Resource] interface {
Resource(name string) (T, error)
ReplaceAll(resources map[Name]T) error
Add(resName Name, res T) error
Remove(name Name) error
ReplaceOne(resName Name, res T) error
}
type apiResourceCollection[T Resource] struct {
api API
mu sync.RWMutex
resources map[string]T
shortNames map[string]string
}
// NewEmptyAPIResourceCollection creates a new API resource collection, which holds and replaces resources belonging to that api.
func NewEmptyAPIResourceCollection[T Resource](api API) APIResourceCollection[T] {
return &apiResourceCollection[T]{
api: api,
resources: map[string]T{},
shortNames: map[string]string{},
}
}
// NewAPIResourceCollection creates a new API resource collection, which holds and replaces resources belonging to that api.
func NewAPIResourceCollection[T Resource](api API, r map[Name]T) (APIResourceCollection[T], error) {
s := &apiResourceCollection[T]{api: api}
if err := s.ReplaceAll(r); err != nil {
return nil, err
}
return s, nil
}
// Resource returns resource by name, if it exists.
func (s *apiResourceCollection[T]) Resource(name string) (T, error) {
s.mu.RLock()
defer s.mu.RUnlock()
if resource, ok := s.resources[name]; ok {
return resource, nil
}
// looking for remote resource matching the name
if resource, ok := s.resources[s.shortNames[name]]; ok {
return resource, nil
}
var zero T
return zero, NewNotFoundError(NewName(s.api, name))
}
// ReplaceAll replaces all resources with r.
func (s *apiResourceCollection[T]) ReplaceAll(r map[Name]T) error {
s.mu.Lock()
defer s.mu.Unlock()
resources := make(map[string]T, len(r))
shortNames := make(map[string]string, len(r))
s.resources = resources
s.shortNames = shortNames
for k, v := range r {
if err := s.doAdd(k, v); err != nil {
return err
}
}
return nil
}
func (s *apiResourceCollection[T]) Add(resName Name, res T) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.doAdd(resName, res)
}
func (s *apiResourceCollection[T]) Remove(n Name) error {
s.mu.Lock()
defer s.mu.Unlock()
return s.doRemove(n)
}
func (s *apiResourceCollection[T]) ReplaceOne(resName Name, res T) error {
s.mu.Lock()
defer s.mu.Unlock()
err := s.doRemove(resName)
if err != nil {
return err
}
return s.doAdd(resName, res)
}
func (s *apiResourceCollection[T]) doAdd(resName Name, res T) error {
if resName.Name == "" {
return errors.Errorf("empty name used for resource: %s", resName)
}
name := resName.ShortName()
_, exists := s.resources[name]
if exists {
return errors.Errorf("resource %s already exists", resName)
}
s.resources[name] = res
shortcut := getShortcutName(name)
if shortcut != name {
if _, ok := s.shortNames[shortcut]; ok {
s.shortNames[shortcut] = ""
} else {
s.shortNames[shortcut] = name
}
}
return nil
}
func (s *apiResourceCollection[T]) doRemove(n Name) error {
name := n.ShortName()
_, ok := s.resources[name]
if !ok {
return errors.Errorf("resource %s not found", name)
}
delete(s.resources, name)
shortcut := getShortcutName(name)
_, ok = s.shortNames[shortcut]
if ok {
delete(s.shortNames, shortcut)
}
// case: remote1:nameA and remote2:nameA both existed, and remote2:nameA is being deleted, restore shortcut to remote1:nameA
for k := range s.resources {
if shortcut == getShortcutName(k) && name != getShortcutName(k) {
if _, ok := s.shortNames[shortcut]; ok {
s.shortNames[shortcut] = ""
} else {
s.shortNames[shortcut] = k
}
}
}
return nil
}
func getShortcutName(name string) string {
return name[strings.LastIndexAny(name, ":")+1:]
}