Skip to content

Commit 0bd0c5f

Browse files
committed
Small refactor to avoid loose json
1 parent 7e4e8f1 commit 0bd0c5f

File tree

6 files changed

+73
-84
lines changed

6 files changed

+73
-84
lines changed

internal/datastore/badgerdb.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ type Store struct {
1818
}
1919

2020
type Entry struct {
21-
Resource k8s.Resource `json:"resource"`
22-
Suspended bool `json:"suspended"`
23-
UpdatedBy string `json:"updatedBy"`
24-
UpdatedAt time.Time `json:"updatedAt"`
21+
Resource k8s.ResourceReference `json:"resource"`
22+
Suspended bool `json:"suspended"`
23+
UpdatedBy string `json:"updatedBy"`
24+
UpdatedAt time.Time `json:"updatedAt"`
2525
}
2626

2727
func NewBadgerStore(path string) (*Store, error) {
@@ -37,7 +37,7 @@ func NewBadgerStore(path string) (*Store, error) {
3737
}, nil
3838
}
3939

40-
func (s *Store) GetEntry(resource k8s.Resource) (Entry, error) {
40+
func (s *Store) GetEntry(resource k8s.ResourceReference) (Entry, error) {
4141
var entry Entry
4242
err := s.db.View(func(txn *badger.Txn) error {
4343
item, err := txn.Get(buildKey(resource))
@@ -73,6 +73,6 @@ func (s *Store) Close() error {
7373
return s.db.Close()
7474
}
7575

76-
func buildKey(resource k8s.Resource) []byte {
76+
func buildKey(resource k8s.ResourceReference) []byte {
7777
return []byte(fmt.Sprintf("resource:%s:%s:%s:%s", resource.Type.Group, resource.Type.Kind, resource.Namespace, resource.Name))
7878
}

internal/fluxcd/resource.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package fluxcd
2+
3+
type Resource struct {
4+
Metadata struct {
5+
Name string `json:"name"`
6+
Namespace string `json:"namespace"`
7+
} `json:"metadata"`
8+
Spec struct {
9+
Suspend bool `json:"suspend"`
10+
} `json:"spec"`
11+
}
12+
13+
type ResourceList struct {
14+
Items []Resource `json:"items"`
15+
}

internal/k8s/client.go

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package k8s
22

33
import (
44
"context"
5-
"encoding/json"
6-
"fmt"
75
"log/slog"
86
"path"
97

@@ -50,7 +48,7 @@ func NewClient(configPath string) (*Client, error) {
5048
}, nil
5149
}
5250

53-
func (c *Client) GetRawResource(ctx context.Context, resource Resource) (map[string]any, error) {
51+
func (c *Client) GetRawResource(ctx context.Context, resource ResourceReference) ([]byte, error) {
5452
absPath := path.Join(
5553
"apis",
5654
resource.Type.Group,
@@ -60,39 +58,27 @@ func (c *Client) GetRawResource(ctx context.Context, resource Resource) (map[str
6058
resource.Type.Kind,
6159
resource.Name,
6260
)
63-
6461
body, err := c.client.RESTClient().Get().AbsPath(absPath).DoRaw(ctx)
6562
if err != nil {
6663
slog.Warn("failed to fetch resource", slog.Any("error", err), slog.Any("path", absPath))
6764
return nil, err
6865
}
69-
70-
var res map[string]any
71-
if err = json.Unmarshal(body, &res); err != nil {
72-
return nil, fmt.Errorf("invalid response: %w", err)
73-
}
74-
return res, nil
66+
return body, nil
7567
}
7668

77-
func (c *Client) GetRawResources(ctx context.Context, group ResourceType) (map[string]any, error) {
69+
func (c *Client) GetRawResources(ctx context.Context, group ResourceType) ([]byte, error) {
7870
absPath := path.Join(
7971
"apis",
8072
group.Group,
8173
group.Version,
8274
group.Kind,
8375
)
84-
8576
body, err := c.client.RESTClient().Get().AbsPath(absPath).DoRaw(ctx)
8677
if err != nil {
87-
slog.Warn("failed to fetch resource", slog.Any("error", err), slog.Any("path", absPath))
78+
slog.Warn("failed to fetch resources", slog.Any("error", err), slog.Any("path", absPath))
8879
return nil, err
8980
}
90-
91-
var res map[string]any
92-
if err = json.Unmarshal(body, &res); err != nil {
93-
return nil, fmt.Errorf("invalid response: %w", err)
94-
}
95-
return res, nil
81+
return body, nil
9682
}
9783

9884
func (c *Client) GetCustomResourceDefinitions(ctx context.Context, listOptions metav1.ListOptions) (*v1.CustomResourceDefinitionList, error) {

internal/k8s/resource.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ type ResourceType struct {
1111
Kind string `json:"kind"`
1212
}
1313

14-
type Resource struct {
14+
type ResourceReference struct {
1515
Type ResourceType `json:"type"`
1616
Namespace string `json:"namespace"`
1717
Name string `json:"name"`
1818
}
1919

20-
func ResourceFromPath(path string) (Resource, error) {
20+
func ResourceReferenceFromPath(path string) (ResourceReference, error) {
2121
parts := strings.Split(path, "/")
2222
if len(parts) != 6 {
23-
return Resource{}, fmt.Errorf("unexpected path format: %s", path)
23+
return ResourceReference{}, fmt.Errorf("unexpected path format: %s", path)
2424
}
25-
return Resource{
25+
return ResourceReference{
2626
Type: ResourceType{
2727
Group: parts[0],
2828
Version: parts[1],

internal/notification/notifier.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
)
88

99
type Notification struct {
10-
Resource k8s.Resource
10+
Resource k8s.ResourceReference
1111
Suspended bool
1212
Email string
1313
GoogleCloudProjectID string

internal/watch/watcher.go

Lines changed: 42 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package watch
22

33
import (
44
"context"
5+
"encoding/json"
56
"errors"
67
"fmt"
78
"log/slog"
@@ -14,6 +15,7 @@ import (
1415

1516
"github.com/e-flux-platform/fluxcd-suspend-notifier/internal/auditlog"
1617
"github.com/e-flux-platform/fluxcd-suspend-notifier/internal/datastore"
18+
"github.com/e-flux-platform/fluxcd-suspend-notifier/internal/fluxcd"
1719
"github.com/e-flux-platform/fluxcd-suspend-notifier/internal/k8s"
1820
"github.com/e-flux-platform/fluxcd-suspend-notifier/internal/notification"
1921
)
@@ -43,13 +45,13 @@ func NewWatcher(
4345
}
4446

4547
type k8sClient interface {
46-
GetRawResource(ctx context.Context, resource k8s.Resource) (map[string]any, error)
47-
GetRawResources(ctx context.Context, group k8s.ResourceType) (map[string]any, error)
48+
GetRawResource(ctx context.Context, resource k8s.ResourceReference) ([]byte, error)
49+
GetRawResources(ctx context.Context, group k8s.ResourceType) ([]byte, error)
4850
GetCustomResourceDefinitions(ctx context.Context, listOptions metav1.ListOptions) (*v1.CustomResourceDefinitionList, error)
4951
}
5052

5153
type store interface {
52-
GetEntry(k8s.Resource) (datastore.Entry, error)
54+
GetEntry(k8s.ResourceReference) (datastore.Entry, error)
5355
SaveEntry(datastore.Entry) error
5456
}
5557

@@ -90,33 +92,21 @@ func (w *Watcher) Watch(ctx context.Context) error {
9092
func (w *Watcher) init(ctx context.Context, groups []k8s.ResourceType) error {
9193
slog.Info("initializing")
9294
for _, group := range groups {
93-
resources, err := w.k8sClient.GetRawResources(ctx, group)
95+
res, err := w.k8sClient.GetRawResources(ctx, group)
9496
if err != nil {
9597
return err
9698
}
97-
items, ok := resources["items"].([]any)
98-
if !ok {
99-
return errors.New("expected items to be set")
99+
var resourceList fluxcd.ResourceList
100+
if err = json.Unmarshal(res, &resourceList); err != nil {
101+
return fmt.Errorf("failed to unmarshal resource: %w", err)
100102
}
101-
for _, i := range items {
102-
item, ok := i.(map[string]any)
103-
if !ok {
104-
return errors.New("invalid item")
105-
}
106-
spec, ok := item["spec"].(map[string]any)
107-
if !ok {
108-
return errors.New("invalid spec")
109-
}
110-
metadata, ok := item["metadata"].(map[string]any)
111-
if !ok {
112-
return errors.New("invalid metadata")
113-
}
114-
resource := k8s.Resource{
103+
for _, resource := range resourceList.Items {
104+
resourceRef := k8s.ResourceReference{
115105
Type: group,
116-
Namespace: metadata["namespace"].(string),
117-
Name: metadata["name"].(string),
106+
Namespace: resource.Metadata.Namespace,
107+
Name: resource.Metadata.Name,
118108
}
119-
if err = w.processResource(ctx, resource, spec, "<unknown>"); err != nil {
109+
if err = w.processResource(ctx, resourceRef, resource, "<unknown>"); err != nil {
120110
return fmt.Errorf("failed to process resource: %w", err)
121111
}
122112
}
@@ -140,22 +130,22 @@ func (w *Watcher) watch(ctx context.Context, groups []k8s.ResourceType) error {
140130
resourceName := logEntry.GetResourceName()
141131
email := logEntry.GetAuthenticationInfo().GetPrincipalEmail()
142132

143-
resource, err := k8s.ResourceFromPath(resourceName)
133+
resourceRef, err := k8s.ResourceReferenceFromPath(resourceName)
144134
if err != nil {
145135
return err
146136
}
147137

148-
res, err := w.k8sClient.GetRawResource(ctx, resource)
138+
res, err := w.k8sClient.GetRawResource(ctx, resourceRef)
149139
if err != nil {
150140
return fmt.Errorf("failed to get raw resource: %w", err)
151141
}
152142

153-
spec, ok := res["spec"].(map[string]any)
154-
if !ok {
155-
return errors.New("unexpected response payload")
143+
var resource fluxcd.Resource
144+
if err = json.Unmarshal(res, &resource); err != nil {
145+
return fmt.Errorf("failed to unmarshal resource: %w", err)
156146
}
157147

158-
if err = w.processResource(ctx, resource, spec, email); err != nil {
148+
if err = w.processResource(ctx, resourceRef, resource, email); err != nil {
159149
return fmt.Errorf("failed to re-check suspension status: %w", err)
160150
}
161151

@@ -165,26 +155,24 @@ func (w *Watcher) watch(ctx context.Context, groups []k8s.ResourceType) error {
165155

166156
func (w *Watcher) processResource(
167157
ctx context.Context,
168-
resource k8s.Resource,
169-
spec map[string]any,
158+
resourceRef k8s.ResourceReference,
159+
resource fluxcd.Resource,
170160
updatedBy string,
171161
) error {
172-
suspended, _ := spec["suspend"].(bool)
173-
174-
entry, err := w.store.GetEntry(resource)
162+
entry, err := w.store.GetEntry(resourceRef)
175163
if err != nil {
176164
if errors.Is(err, datastore.ErrNotFound) {
177165
// First time seeing the resource, so we'll save the state, but not notify - as we don't know what has
178166
// changed
179167
slog.Info(
180168
"new resource discovered",
181-
slog.String("kind", resource.Type.Kind),
182-
slog.String("resource", resource.Name),
183-
slog.Bool("suspended", suspended),
169+
slog.String("kind", resourceRef.Type.Kind),
170+
slog.String("resource", resourceRef.Name),
171+
slog.Bool("suspended", resource.Spec.Suspend),
184172
)
185173
if err = w.store.SaveEntry(datastore.Entry{
186-
Resource: resource,
187-
Suspended: suspended,
174+
Resource: resourceRef,
175+
Suspended: resource.Spec.Suspend,
188176
UpdatedBy: updatedBy,
189177
UpdatedAt: time.Now().UTC(),
190178
}); err != nil {
@@ -195,31 +183,31 @@ func (w *Watcher) processResource(
195183
return fmt.Errorf("failed to fetch entry: %w", err)
196184
}
197185

198-
if suspended == entry.Suspended {
186+
if resource.Spec.Suspend == entry.Suspended {
199187
return nil // Probably something else about the resource modified
200188
}
201189

202-
entry.Resource = resource
203-
entry.Suspended = suspended
190+
slog.Info(
191+
"suspension status updated",
192+
slog.String("kind", resourceRef.Type.Kind),
193+
slog.String("resourceRef", resourceRef.Name),
194+
slog.String("user", updatedBy),
195+
slog.Bool("suspended", resource.Spec.Suspend),
196+
)
197+
198+
entry.Resource = resourceRef
199+
entry.Suspended = resource.Spec.Suspend
204200
entry.UpdatedBy = updatedBy
205201
entry.UpdatedAt = time.Now().UTC()
206202

207203
if err = w.store.SaveEntry(entry); err != nil {
208204
return err
209205
}
210206

211-
slog.Info(
212-
"suspension status updated",
213-
slog.String("kind", resource.Type.Kind),
214-
slog.String("resource", resource.Name),
215-
slog.String("user", updatedBy),
216-
slog.Bool("suspended", suspended),
217-
)
218-
219207
return w.notifier.Notify(ctx, notification.Notification{
220-
Resource: resource,
221-
Suspended: suspended,
222-
Email: updatedBy,
208+
Resource: entry.Resource,
209+
Suspended: entry.Suspended,
210+
Email: entry.UpdatedBy,
223211
GoogleCloudProjectID: w.googleCloudProjectID,
224212
})
225213
}

0 commit comments

Comments
 (0)