Skip to content

Commit f5fb2ff

Browse files
authored
fix: prevent overwriting schema in PreReconcile (#234)
* fix: skip schema write if unchanged in PreReconcile * feat: added test for prereconcile * fix: if the schema is missing then create the file instead of failing with an error
1 parent 11be0b1 commit f5fb2ff

File tree

3 files changed

+57
-3
lines changed

3 files changed

+57
-3
lines changed

listener/kcp/reconciler_factory.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package kcp
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67

@@ -35,6 +36,7 @@ var (
3536
ErrCreatePathResolver = errors.New("failed to create cluster path resolver")
3637
ErrGetVWConfig = errors.New("unable to get virtual workspace config, check if your kcp cluster is running")
3738
ErrCreateHTTPClient = errors.New("failed to create http client")
39+
ErrReadJSON = errors.New("failed to read JSON from filesystem")
3840
)
3941

4042
type CustomReconciler interface {
@@ -107,12 +109,23 @@ func PreReconcile(
107109
cr *apischema.CRDResolver,
108110
io workspacefile.IOHandler,
109111
) error {
110-
JSON, err := cr.Resolve()
112+
actualJSON, err := cr.Resolve()
111113
if err != nil {
112114
return errors.Join(ErrResolveSchema, err)
113115
}
114-
if err := io.Write(JSON, kubernetesClusterName); err != nil {
115-
return errors.Join(ErrWriteJSON, err)
116+
117+
savedJSON, err := io.Read(kubernetesClusterName)
118+
if err != nil {
119+
if errors.Is(err, workspacefile.ErrNotFound) {
120+
return io.Write(actualJSON, kubernetesClusterName)
121+
}
122+
return errors.Join(ErrReadJSON, err)
123+
}
124+
125+
if !bytes.Equal(actualJSON, savedJSON) {
126+
if err := io.Write(actualJSON, kubernetesClusterName); err != nil {
127+
return errors.Join(ErrWriteJSON, err)
128+
}
116129
}
117130

118131
return nil

listener/kcp/reconciler_factory_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,40 @@ func TestNewReconciler(t *testing.T) {
106106
})
107107
}
108108
}
109+
110+
func TestPreReconcile(t *testing.T) {
111+
tempDir := t.TempDir()
112+
113+
tests := map[string]struct {
114+
cr *apischema.CRDResolver
115+
err error
116+
}{
117+
"error_on_empty_resolver": {
118+
cr: func() *apischema.CRDResolver {
119+
discovery := &mocks.MockDiscoveryInterface{}
120+
discovery.On("ServerPreferredResources").Return(nil, errors.New("failed to get server resources"))
121+
122+
return &apischema.CRDResolver{
123+
DiscoveryInterface: discovery,
124+
RESTMapper: &mocks.MockRESTMapper{},
125+
}
126+
}(),
127+
err: errors.Join(ErrResolveSchema,
128+
errors.New("failed to get server preferred resources"),
129+
errors.New("failed to get server resources")),
130+
},
131+
}
132+
133+
for name, tc := range tests {
134+
t.Run(name, func(t *testing.T) {
135+
ioHandler, err := workspacefile.NewIOHandler(tempDir)
136+
assert.NoError(t, err)
137+
err = PreReconcile(tc.cr, ioHandler)
138+
if tc.err != nil {
139+
assert.EqualError(t, err, tc.err.Error())
140+
} else {
141+
assert.NoError(t, err)
142+
}
143+
})
144+
}
145+
}

listener/workspacefile/io_handler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var (
1111
ErrReadJSONFile = errors.New("failed to read JSON file")
1212
ErrWriteJSONFile = errors.New("failed to write JSON to file")
1313
ErrDeleteJSONFile = errors.New("failed to delete JSON file")
14+
ErrNotFound = errors.New("failed to find JSON file")
1415
)
1516

1617
type IOHandler interface {
@@ -37,6 +38,9 @@ func (h *IOHandlerProvider) Read(clusterName string) ([]byte, error) {
3738
fileName := path.Join(h.schemasDir, clusterName)
3839
JSON, err := os.ReadFile(fileName)
3940
if err != nil {
41+
if os.IsNotExist(err) {
42+
return nil, ErrNotFound
43+
}
4044
return nil, errors.Join(ErrReadJSONFile, err)
4145
}
4246
return JSON, nil

0 commit comments

Comments
 (0)