• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Twingate / terraform-provider-twingate / 16979793061

15 Aug 2025 12:24AM UTC coverage: 90.068% (-0.08%) from 90.151%
16979793061

Pull #754

github

web-flow
Bump securego/gosec from 2.22.7 to 2.22.8 in /tools

Bumps securego/gosec from 2.22.7 to 2.22.8.

---
updated-dependencies:
- dependency-name: securego/gosec
  dependency-version: 2.22.8
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #754: Bump securego/gosec from 2.22.7 to 2.22.8 in /tools

8697 of 9656 relevant lines covered (90.07%)

2.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.72
/twingate/internal/client/cache.go
1
package client
2

3
import (
4
        "context"
5
        "log"
6
        "reflect"
7
        "sync"
8

9
        "github.com/Twingate/terraform-provider-twingate/v3/twingate/internal/model"
10
        "github.com/mitchellh/copystructure"
11
        "golang.org/x/sync/errgroup"
12
)
13

14
const cacheKey = "cache"
15

16
type CacheOptions struct {
17
        ResourceEnabled bool
18
        GroupsEnabled   bool
19
}
20

21
var cache = &clientCache{} //nolint:gochecknoglobals
22

23
type clientCache struct {
24
        once     sync.Once
25
        handlers map[string]resourceHandler
26
}
27

28
type ReadClient interface {
29
        ReadFullResources(ctx context.Context) ([]*model.Resource, error)
30
        ReadFullGroups(ctx context.Context) ([]*model.Group, error)
31
}
32

33
func (c *clientCache) setClient(client ReadClient, opts CacheOptions) {
3✔
34
        c.once.Do(func() {
6✔
35
                c.handlers = map[string]resourceHandler{
3✔
36
                        reflect.TypeOf(&model.Resource{}).String(): &handler[*model.Resource]{
3✔
37
                                enabled:       opts.ResourceEnabled,
3✔
38
                                readResources: client.ReadFullResources,
3✔
39
                        },
3✔
40
                        reflect.TypeOf(&model.Group{}).String(): &handler[*model.Group]{
3✔
41
                                enabled:       opts.GroupsEnabled,
3✔
42
                                readResources: client.ReadFullGroups,
3✔
43
                        },
3✔
44
                }
3✔
45

3✔
46
                group := errgroup.Group{}
3✔
47

3✔
48
                for handlerType, handler := range c.handlers {
6✔
49
                        group.Go(func() error {
6✔
50
                                if handler.isEnabled() {
5✔
51
                                        return handler.init()
2✔
52
                                }
2✔
53

54
                                log.Printf("[DEBUG] cache init for type %v: skipped.", handlerType)
3✔
55

3✔
56
                                return nil
3✔
57
                        })
58
                }
59

60
                if err := group.Wait(); err != nil {
3✔
61
                        log.Printf("[ERR] cache init failed: %s", err.Error())
×
62
                }
×
63
        })
64
}
65

66
type resourceHandler interface {
67
        isEnabled() bool
68
        init() error
69
        getResource(resourceID string) (any, bool)
70
        setResource(resource identifiable)
71
        invalidateResource(resourceID string)
72
        matchResources(filter model.ResourceFilter) []any
73
}
74

75
type identifiable interface {
76
        GetID() string
77
        Match(filter model.ResourceFilter) bool
78
}
79

80
type readResourcesFunc[T identifiable] func(ctx context.Context) ([]T, error)
81

82
type handler[T identifiable] struct {
83
        once    sync.Once
84
        enabled bool
85

86
        resources     sync.Map
87
        readResources readResourcesFunc[T]
88
}
89

90
func (h *handler[T]) isEnabled() bool {
3✔
91
        return h.enabled
3✔
92
}
3✔
93

94
func (h *handler[T]) getResource(resourceID string) (any, bool) {
3✔
95
        var emptyObj T
3✔
96

3✔
97
        if h.readResources == nil {
3✔
98
                return emptyObj, false
×
99
        }
×
100

101
        res, exists := h.resources.Load(resourceID)
3✔
102

3✔
103
        if !exists {
6✔
104
                return emptyObj, false
3✔
105
        }
3✔
106

107
        obj, err := copystructure.Copy(res)
3✔
108

3✔
109
        if err != nil {
3✔
110
                log.Printf("[ERR] %T failed copy object from cache: %s", emptyObj, err.Error())
×
111

×
112
                return emptyObj, false
×
113
        }
×
114

115
        return obj, exists
3✔
116
}
117

118
func (h *handler[T]) matchResources(filter model.ResourceFilter) []any {
3✔
119
        var matched []any
3✔
120

3✔
121
        h.resources.Range(func(key, value any) bool {
6✔
122
                obj := value.(T)
3✔
123
                if obj.Match(filter) {
6✔
124
                        matched = append(matched, obj)
3✔
125
                }
3✔
126

127
                return true
3✔
128
        })
129

130
        return matched
3✔
131
}
132

133
func (h *handler[T]) setResource(resource identifiable) {
3✔
134
        if resource == nil {
3✔
135
                return
×
136
        }
×
137

138
        defer func() {
6✔
139
                if r := recover(); r != nil {
3✔
140
                        log.Printf("[ERR] setResource failed: %v", r)
×
141
                }
×
142
        }()
143

144
        obj, err := copystructure.Copy(resource)
3✔
145

3✔
146
        if err != nil {
3✔
147
                log.Printf("[ERR] %T failed store object to cache: %s", resource, err.Error())
×
148

×
149
                return
×
150
        }
×
151

152
        h.resources.Store(resource.GetID(), obj)
3✔
153
}
154

155
func (h *handler[T]) setResources(resources []T) {
3✔
156
        for _, resource := range resources {
6✔
157
                h.setResource(resource)
3✔
158
        }
3✔
159
}
160

161
func (h *handler[T]) invalidateResource(id string) {
3✔
162
        h.resources.Delete(id)
3✔
163
}
3✔
164

165
func (h *handler[T]) init() error {
3✔
166
        var initErr error
3✔
167

3✔
168
        h.once.Do(func() {
6✔
169
                var (
3✔
170
                        res T
3✔
171
                )
3✔
172

3✔
173
                log.Printf("[DEBUG] cache init for type %T: started.", res)
3✔
174

3✔
175
                resources, err := h.readResources(WithCallerCtx(context.Background(), cacheKey))
3✔
176
                if err != nil {
3✔
177
                        log.Printf("[ERR] cache init for type %T failed: %s", res, err.Error())
×
178

×
179
                        initErr = err
×
180

×
181
                        return
×
182
                }
×
183

184
                h.setResources(resources)
3✔
185

3✔
186
                log.Printf("[DEBUG] cache init for type %T: finished.", res)
3✔
187
        })
188

189
        return initErr
3✔
190
}
191

192
func getResource[T any](resourceID string) (T, bool) {
3✔
193
        var (
3✔
194
                res    T
3✔
195
                exists bool
3✔
196
        )
3✔
197

3✔
198
        handle(res, func(handler resourceHandler) {
5✔
199
                resource, found := handler.getResource(resourceID)
2✔
200
                if !found || resource == nil {
4✔
201
                        return
2✔
202
                }
2✔
203

204
                obj, ok := resource.(T)
2✔
205
                if !ok {
2✔
206
                        log.Printf("[ERR] getResource failed: expected type %T, got %T", res, resource)
×
207

×
208
                        return
×
209
                }
×
210

211
                res = obj
2✔
212
                exists = found
2✔
213
        })
214

215
        return res, exists
3✔
216
}
217

218
func setResource(resource identifiable) {
3✔
219
        handle(resource, func(handler resourceHandler) {
5✔
220
                handler.setResource(resource)
2✔
221
        })
2✔
222
}
223

224
func invalidateResource[T any](resourceID string) {
3✔
225
        var res T
3✔
226

3✔
227
        handle(res, func(handler resourceHandler) {
5✔
228
                handler.invalidateResource(resourceID)
2✔
229
        })
2✔
230
}
231

232
func handle(handlerType any, apply func(handler resourceHandler)) {
3✔
233
        if handler, ok := cache.handlers[handlerKey(handlerType)]; ok {
5✔
234
                apply(handler)
2✔
235
        }
2✔
236
}
237

238
func handlerKey(handlerType any) string {
3✔
239
        return reflect.TypeOf(handlerType).String()
3✔
240
}
3✔
241

242
func matchResources[T any](filter model.ResourceFilter) []T {
3✔
243
        var (
3✔
244
                res     T
3✔
245
                matched []T
3✔
246
        )
3✔
247

3✔
248
        handle(res, func(handler resourceHandler) {
5✔
249
                resources := handler.matchResources(filter)
2✔
250
                for _, resource := range resources {
4✔
251
                        obj, ok := resource.(T)
2✔
252
                        if !ok {
2✔
253
                                log.Printf("[ERR] matchResources failed: expected type %T, got %T", res, resource)
×
254

×
255
                                return
×
256
                        }
×
257

258
                        matched = append(matched, obj)
2✔
259
                }
260
        })
261

262
        return matched
3✔
263
}
264

265
func lazyLoadResources[T any]() {
3✔
266
        var (
3✔
267
                res T
3✔
268
        )
3✔
269

3✔
270
        handle(res, func(handler resourceHandler) {
5✔
271
                if err := handler.init(); err != nil {
2✔
272
                        log.Printf("[ERR] lazyLoadResources for type %T failed: %s", res, err.Error())
×
273
                }
×
274
        })
275
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc