Skip to content

Commit 2d51c5a

Browse files
committed
add mutation tracking to the fake client
1 parent d7430e4 commit 2d51c5a

File tree

7 files changed

+775
-32
lines changed

7 files changed

+775
-32
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package manifestclient
2+
3+
import (
4+
"k8s.io/apimachinery/pkg/runtime/schema"
5+
"k8s.io/apimachinery/pkg/util/sets"
6+
"sync"
7+
)
8+
9+
type AllActionsTracker struct {
10+
lock sync.RWMutex
11+
12+
ActionToTracker map[Action]*ActionTracker
13+
}
14+
15+
type Action string
16+
17+
const (
18+
// this is really a subset of patch, but we treat it separately because it is useful to do so
19+
ActionApply Action = "server-side-apply"
20+
ActionApplyStatus Action = "server-side-apply-status"
21+
ActionUpdate Action = "update"
22+
ActionUpdateStatus Action = "update-status"
23+
ActionCreate Action = "create"
24+
ActionDelete Action = "delete"
25+
)
26+
27+
type ActionMetadata struct {
28+
Action Action
29+
GVR schema.GroupVersionResource
30+
Namespace string
31+
Name string
32+
}
33+
34+
type ActionTracker struct {
35+
Action Action
36+
ResourceToTracker map[schema.GroupVersionResource]*ResourceTracker
37+
}
38+
39+
type ResourceTracker struct {
40+
GVR schema.GroupVersionResource
41+
NamespaceToTracker map[string]*NamespaceTracker
42+
}
43+
44+
type NamespaceTracker struct {
45+
Namespace string
46+
NameToTracker map[string]*NameTracker
47+
}
48+
49+
type NameTracker struct {
50+
Name string
51+
SerializedRequests []SerializedRequest
52+
}
53+
54+
type SerializedRequest struct {
55+
Options []byte
56+
Body []byte
57+
}
58+
59+
func (a *AllActionsTracker) AddRequest(metadata ActionMetadata, request SerializedRequest) {
60+
a.lock.Lock()
61+
defer a.lock.Unlock()
62+
63+
if a.ActionToTracker == nil {
64+
a.ActionToTracker = map[Action]*ActionTracker{}
65+
}
66+
if _, ok := a.ActionToTracker[metadata.Action]; !ok {
67+
a.ActionToTracker[metadata.Action] = &ActionTracker{Action: metadata.Action}
68+
}
69+
a.ActionToTracker[metadata.Action].AddRequest(metadata, request)
70+
}
71+
72+
func (a *AllActionsTracker) ListActions() []Action {
73+
a.lock.Lock()
74+
defer a.lock.Unlock()
75+
76+
return sets.KeySet(a.ActionToTracker).UnsortedList()
77+
}
78+
79+
func (a *AllActionsTracker) MutationsForAction(action Action) *ActionTracker {
80+
a.lock.RLock()
81+
defer a.lock.RUnlock()
82+
83+
return a.ActionToTracker[action]
84+
}
85+
86+
func (a *AllActionsTracker) MutationsForMetadata(metadata ActionMetadata) []SerializedRequest {
87+
a.lock.RLock()
88+
defer a.lock.RUnlock()
89+
90+
actionTracker := a.MutationsForAction(metadata.Action)
91+
if actionTracker == nil {
92+
return nil
93+
}
94+
resourceTracker := actionTracker.MutationsForResource(metadata.GVR)
95+
if resourceTracker == nil {
96+
return nil
97+
}
98+
namespaceTracker := resourceTracker.MutationsForNamespace(metadata.Namespace)
99+
if namespaceTracker == nil {
100+
return nil
101+
}
102+
nameTracker := namespaceTracker.MutationsForName(metadata.Name)
103+
if nameTracker == nil {
104+
return nil
105+
}
106+
return nameTracker.SerializedRequests
107+
}
108+
109+
func (a *ActionTracker) AddRequest(metadata ActionMetadata, request SerializedRequest) {
110+
if a.ResourceToTracker == nil {
111+
a.ResourceToTracker = map[schema.GroupVersionResource]*ResourceTracker{}
112+
}
113+
if _, ok := a.ResourceToTracker[metadata.GVR]; !ok {
114+
a.ResourceToTracker[metadata.GVR] = &ResourceTracker{GVR: metadata.GVR}
115+
}
116+
a.ResourceToTracker[metadata.GVR].AddRequest(metadata, request)
117+
}
118+
119+
func (a *ActionTracker) ListResources() []schema.GroupVersionResource {
120+
return sets.KeySet(a.ResourceToTracker).UnsortedList()
121+
}
122+
123+
func (a *ActionTracker) MutationsForResource(gvr schema.GroupVersionResource) *ResourceTracker {
124+
return a.ResourceToTracker[gvr]
125+
}
126+
127+
func (a *ResourceTracker) AddRequest(metadata ActionMetadata, request SerializedRequest) {
128+
if a.NamespaceToTracker == nil {
129+
a.NamespaceToTracker = map[string]*NamespaceTracker{}
130+
}
131+
if _, ok := a.NamespaceToTracker[metadata.Namespace]; !ok {
132+
a.NamespaceToTracker[metadata.Namespace] = &NamespaceTracker{Namespace: metadata.Namespace}
133+
}
134+
a.NamespaceToTracker[metadata.Namespace].AddRequest(metadata, request)
135+
}
136+
137+
func (a *ResourceTracker) ListNamespaces() []string {
138+
return sets.KeySet(a.NamespaceToTracker).UnsortedList()
139+
}
140+
141+
func (a *ResourceTracker) MutationsForNamespace(namespace string) *NamespaceTracker {
142+
return a.NamespaceToTracker[namespace]
143+
}
144+
145+
func (a *NamespaceTracker) AddRequest(metadata ActionMetadata, request SerializedRequest) {
146+
if a.NameToTracker == nil {
147+
a.NameToTracker = map[string]*NameTracker{}
148+
}
149+
if _, ok := a.NameToTracker[metadata.Name]; !ok {
150+
a.NameToTracker[metadata.Name] = &NameTracker{Name: metadata.Name}
151+
}
152+
a.NameToTracker[metadata.Name].AddRequest(request)
153+
}
154+
155+
func (a *NamespaceTracker) ListNames() []string {
156+
return sets.KeySet(a.NameToTracker).UnsortedList()
157+
}
158+
159+
func (a *NamespaceTracker) MutationsForName(name string) *NameTracker {
160+
return a.NameToTracker[name]
161+
}
162+
163+
func (a *NameTracker) AddRequest(request SerializedRequest) {
164+
if a.SerializedRequests == nil {
165+
a.SerializedRequests = []SerializedRequest{}
166+
}
167+
a.SerializedRequests = append(a.SerializedRequests, request)
168+
}
Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,13 @@ type RawReader interface {
3535
fs.ReadDirFS
3636
}
3737

38-
func NewTestingRoundTripper(embedFS embed.FS, prefix string) (*manifestRoundTripper, error) {
39-
return newRoundTripper(newPrefixedReader(embedFS, prefix))
40-
}
41-
42-
func NewRoundTripper(mustGatherDir string) (*manifestRoundTripper, error) {
43-
return newRoundTripper(newMustGatherReader(mustGatherDir))
44-
}
45-
46-
func newRoundTripper(contentReader RawReader) (*manifestRoundTripper, error) {
38+
func newReadRoundTripper(contentReader RawReader) *manifestRoundTripper {
4739
return &manifestRoundTripper{
4840
contentReader: contentReader,
4941
requestInfoResolver: server.NewRequestInfoResolver(&server.Config{
5042
LegacyAPIGroupPrefixes: sets.NewString(server.DefaultLegacyAPIPrefix),
5143
}),
52-
}, nil
44+
}
5345
}
5446

5547
type prefixedContentReader struct {
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package manifestclient
2+
3+
import (
4+
"bytes"
5+
"embed"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
)
10+
11+
// Enter here and call `NewForConfigAndClient(&rest.Config{}, httpClient)`
12+
func NewHTTPClient(mustGatherDir string) MutationTrackingClient {
13+
mutationTrackingRoundTripper := newReadWriteRoundTripper(newMustGatherReader(mustGatherDir))
14+
return &mutationTrackingClient{
15+
httpClient: &http.Client{
16+
Transport: mutationTrackingRoundTripper,
17+
},
18+
mutationTrackingRoundTripper: mutationTrackingRoundTripper,
19+
}
20+
}
21+
22+
// Enter here and call `NewForConfigAndClient(&rest.Config{}, httpClient)`
23+
func NewTestingHTTPClient(embedFS embed.FS, prefix string) MutationTrackingClient {
24+
mutationTrackingRoundTripper := newReadWriteRoundTripper(newPrefixedReader(embedFS, prefix))
25+
return &mutationTrackingClient{
26+
httpClient: &http.Client{
27+
Transport: mutationTrackingRoundTripper,
28+
},
29+
mutationTrackingRoundTripper: mutationTrackingRoundTripper,
30+
}
31+
}
32+
33+
func NewTestingRoundTripper(embedFS embed.FS, prefix string) *readWriteRoundTripper {
34+
return newReadWriteRoundTripper(newPrefixedReader(embedFS, prefix))
35+
}
36+
37+
func NewRoundTripper(mustGatherDir string) *readWriteRoundTripper {
38+
return newReadWriteRoundTripper(newMustGatherReader(mustGatherDir))
39+
}
40+
41+
func newReadWriteRoundTripper(contentReader RawReader) *readWriteRoundTripper {
42+
return &readWriteRoundTripper{
43+
readDelegate: newReadRoundTripper(contentReader),
44+
writeDelegate: newWriteRoundTripper(),
45+
}
46+
}
47+
48+
type readWriteRoundTripper struct {
49+
readDelegate *manifestRoundTripper
50+
writeDelegate *writeTrackingRoundTripper
51+
}
52+
53+
type MutationTrackingRoundTripper interface {
54+
http.RoundTripper
55+
GetMutations() *AllActionsTracker
56+
}
57+
58+
type mutationTrackingClient struct {
59+
httpClient *http.Client
60+
61+
mutationTrackingRoundTripper MutationTrackingRoundTripper
62+
}
63+
64+
func (m mutationTrackingClient) GetHTTPClient() *http.Client {
65+
return m.httpClient
66+
}
67+
68+
func (m mutationTrackingClient) GetMutations() *AllActionsTracker {
69+
return m.mutationTrackingRoundTripper.GetMutations()
70+
}
71+
72+
type MutationTrackingClient interface {
73+
GetHTTPClient() *http.Client
74+
GetMutations() *AllActionsTracker
75+
}
76+
77+
func (rt *readWriteRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
78+
switch req.Method {
79+
case "GET", "HEAD":
80+
return rt.readDelegate.RoundTrip(req)
81+
case "POST", "PUT", "PATCH", "DELETE":
82+
return rt.writeDelegate.RoundTrip(req)
83+
default:
84+
resp := &http.Response{}
85+
resp.StatusCode = http.StatusInternalServerError
86+
resp.Status = http.StatusText(resp.StatusCode)
87+
resp.Body = io.NopCloser(bytes.NewBufferString(fmt.Sprintf("unhandled verb: %q", req.Method)))
88+
return resp, nil
89+
}
90+
}
91+
92+
func (rt *readWriteRoundTripper) GetMutations() *AllActionsTracker {
93+
return rt.writeDelegate.actionTracker
94+
}

0 commit comments

Comments
 (0)