github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/fake/client.go (about) 1 package fake 2 3 import ( 4 "context" 5 "encoding/json" 6 7 "sigs.k8s.io/controller-runtime/pkg/client" 8 ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" 9 10 "github.com/tilt-dev/tilt-apiserver/pkg/server/builder/resource/resourcestrategy" 11 ) 12 13 // Wrap the fake client in other assertions we want to make. 14 type fakeTiltClient struct { 15 ctrlclient.Client 16 } 17 18 func (f fakeTiltClient) Create(ctx context.Context, obj client.Object, opts ...client.CreateOption) error { 19 forCreate := obj.DeepCopyObject().(client.Object) 20 if defaulter, ok := forCreate.(resourcestrategy.Defaulter); ok { 21 defaulter.Default() 22 } 23 24 if err := f.Client.Create(ctx, forCreate, opts...); err != nil { 25 return err 26 } 27 28 return simulateMarshalRoundtrip(forCreate, obj) 29 } 30 31 func (f fakeTiltClient) Update(ctx context.Context, obj client.Object, opts ...client.UpdateOption) error { 32 if ctxErr := ctx.Err(); ctxErr != nil { 33 // controller-runtime fake ignores context; check it here to allow controllers to test 34 // handling of cancellation related errors 35 return ctxErr 36 } 37 38 forUpdate := obj.DeepCopyObject().(client.Object) 39 if defaulter, ok := forUpdate.(resourcestrategy.Defaulter); ok { 40 defaulter.Default() 41 } 42 43 err := f.Client.Update(ctx, forUpdate, opts...) 44 if err != nil { 45 return err 46 } 47 48 return simulateMarshalRoundtrip(forUpdate, obj) 49 } 50 51 func (f fakeTiltClient) Status() client.StatusWriter { 52 return fakeStatusWriter{f.Client.Status()} 53 } 54 55 type fakeStatusWriter struct { 56 ctrlclient.StatusWriter 57 } 58 59 func (f fakeStatusWriter) Update(ctx context.Context, obj client.Object, opts ...client.SubResourceUpdateOption) error { 60 if ctxErr := ctx.Err(); ctxErr != nil { 61 // controller-runtime fake ignores context; check it here to allow controllers to test 62 // handling of cancellation related errors 63 return ctxErr 64 } 65 66 err := f.StatusWriter.Update(ctx, obj, opts...) 67 if err != nil { 68 return err 69 } 70 71 content, err := json.Marshal(obj) 72 if err != nil { 73 return err 74 } 75 return json.Unmarshal(content, obj) 76 } 77 78 func (f fakeStatusWriter) Patch(ctx context.Context, obj ctrlclient.Object, patch ctrlclient.Patch, opts ...ctrlclient.SubResourcePatchOption) error { 79 if ctxErr := ctx.Err(); ctxErr != nil { 80 // controller-runtime fake ignores context; check it here to allow controllers to test 81 // handling of cancellation related errors 82 return ctxErr 83 } 84 85 err := f.StatusWriter.Patch(ctx, obj, patch, opts...) 86 if err != nil { 87 return err 88 } 89 90 content, err := json.Marshal(obj) 91 if err != nil { 92 return err 93 } 94 return json.Unmarshal(content, obj) 95 } 96 97 // simulateMarshalRoundtrip makes API types behave closer to reality for the 98 // fake client. 99 // 100 // NOTE(nick): We've had a lot of bugs due to the way the apiserver 101 // modifies and truncates objects on update. We simulate this behavior 102 // to catch this class of bug. 103 // 104 // The `apiResp` argument is marshaled and then unmarshalled into `dest` arg. 105 // (This allows the fake client to mutate a *copy* of the user-provided obj, and 106 // only mutate the *original* obj here on success.) 107 func simulateMarshalRoundtrip(apiResp, dest ctrlclient.Object) error { 108 content, err := json.Marshal(apiResp) 109 if err != nil { 110 return err 111 } 112 if err := json.Unmarshal(content, dest); err != nil { 113 return err 114 } 115 return nil 116 }