@@ -19,6 +19,7 @@ package rest
19
19
import (
20
20
"bytes"
21
21
"context"
22
+ "encoding/base64"
22
23
"encoding/hex"
23
24
"fmt"
24
25
"io"
@@ -701,14 +702,19 @@ func (b *throttledLogger) Infof(message string, args ...interface{}) {
701
702
// Watch attempts to begin watching the requested location.
702
703
// Returns a watch.Interface, or an error.
703
704
func (r * Request ) Watch (ctx context.Context ) (watch.Interface , error ) {
705
+ w , _ , e := r .watchInternal (ctx )
706
+ return w , e
707
+ }
708
+
709
+ func (r * Request ) watchInternal (ctx context.Context ) (watch.Interface , runtime.Decoder , error ) {
704
710
if r .body == nil {
705
711
logBody (ctx , 2 , "Request Body" , r .bodyBytes )
706
712
}
707
713
708
714
// We specifically don't want to rate limit watches, so we
709
715
// don't use r.rateLimiter here.
710
716
if r .err != nil {
711
- return nil , r .err
717
+ return nil , nil , r .err
712
718
}
713
719
714
720
client := r .c .Client
@@ -728,12 +734,12 @@ func (r *Request) Watch(ctx context.Context) (watch.Interface, error) {
728
734
url := r .URL ().String ()
729
735
for {
730
736
if err := retry .Before (ctx , r ); err != nil {
731
- return nil , retry .WrapPreviousError (err )
737
+ return nil , nil , retry .WrapPreviousError (err )
732
738
}
733
739
734
740
req , err := r .newHTTPRequest (ctx )
735
741
if err != nil {
736
- return nil , err
742
+ return nil , nil , err
737
743
}
738
744
739
745
resp , err := client .Do (req )
@@ -761,14 +767,14 @@ func (r *Request) Watch(ctx context.Context) (watch.Interface, error) {
761
767
}()
762
768
if done {
763
769
if isErrRetryableFunc (req , err ) {
764
- return watch .NewEmptyWatch (), nil
770
+ return watch .NewEmptyWatch (), nil , nil
765
771
}
766
772
if err == nil {
767
773
// if the server sent us an HTTP Response object,
768
774
// we need to return the error object from that.
769
775
err = transformErr
770
776
}
771
- return nil , retry .WrapPreviousError (err )
777
+ return nil , nil , retry .WrapPreviousError (err )
772
778
}
773
779
}
774
780
}
@@ -786,29 +792,52 @@ type WatchListResult struct {
786
792
// the end of the stream.
787
793
initialEventsEndBookmarkRV string
788
794
789
- // gv represents the API version
790
- // it is used to construct the final list response
791
- // normally this information is filled by the server
792
- gv schema.GroupVersion
795
+ // negotiatedObjectDecoder knows how to decode
796
+ // the initialEventsListBlueprint
797
+ negotiatedObjectDecoder runtime.Decoder
798
+
799
+ // base64EncodedInitialEventsListBlueprint contains an empty,
800
+ // versioned list encoded in the requested format
801
+ // (e.g., protobuf, JSON, CBOR) and stored as a base64-encoded string
802
+ base64EncodedInitialEventsListBlueprint string
793
803
}
794
804
805
+ // Into stores the result into obj. The passed obj parameter must be a pointer to a list type.
806
+ //
807
+ // Note:
808
+ //
809
+ // Special attention should be given to the type *unstructured.Unstructured,
810
+ // which represents a list type but does not have an "Items" field.
811
+ // Users who directly use RESTClient may store the response in such an object.
812
+ // This particular case is not handled by the current implementation of this function,
813
+ // but may be considered for future updates.
795
814
func (r WatchListResult ) Into (obj runtime.Object ) error {
796
815
if r .err != nil {
797
816
return r .err
798
817
}
799
818
800
- listPtr , err := meta .GetItemsPtr (obj )
819
+ listItemsPtr , err := meta .GetItemsPtr (obj )
801
820
if err != nil {
802
821
return err
803
822
}
804
- listVal , err := conversion .EnforcePtr (listPtr )
823
+ listVal , err := conversion .EnforcePtr (listItemsPtr )
805
824
if err != nil {
806
825
return err
807
826
}
808
827
if listVal .Kind () != reflect .Slice {
809
828
return fmt .Errorf ("need a pointer to slice, got %v" , listVal .Kind ())
810
829
}
811
830
831
+ encodedInitialEventsListBlueprint , err := base64 .StdEncoding .DecodeString (r .base64EncodedInitialEventsListBlueprint )
832
+ if err != nil {
833
+ return fmt .Errorf ("failed to decode the received blueprint list, err %w" , err )
834
+ }
835
+
836
+ err = runtime .DecodeInto (r .negotiatedObjectDecoder , encodedInitialEventsListBlueprint , obj )
837
+ if err != nil {
838
+ return err
839
+ }
840
+
812
841
if len (r .items ) == 0 {
813
842
listVal .Set (reflect .MakeSlice (listVal .Type (), 0 , 0 ))
814
843
} else {
@@ -826,15 +855,6 @@ func (r WatchListResult) Into(obj runtime.Object) error {
826
855
return err
827
856
}
828
857
listMeta .SetResourceVersion (r .initialEventsEndBookmarkRV )
829
-
830
- typeMeta , err := meta .TypeAccessor (obj )
831
- if err != nil {
832
- return err
833
- }
834
- version := r .gv .String ()
835
- typeMeta .SetAPIVersion (version )
836
- typeMeta .SetKind (reflect .TypeOf (obj ).Elem ().Name ())
837
-
838
858
return nil
839
859
}
840
860
@@ -857,16 +877,16 @@ func (r *Request) WatchList(ctx context.Context) WatchListResult {
857
877
// Most users use the generated client, which handles the proper setting of parameters.
858
878
// We don't have validation for other methods (e.g., the Watch)
859
879
// thus, for symmetry, we haven't added additional checks for the WatchList method.
860
- w , err := r .Watch (ctx )
880
+ w , d , err := r .watchInternal (ctx )
861
881
if err != nil {
862
882
return WatchListResult {err : err }
863
883
}
864
- return r .handleWatchList (ctx , w )
884
+ return r .handleWatchList (ctx , w , d )
865
885
}
866
886
867
887
// handleWatchList holds the actual logic for easier unit testing.
868
888
// Note that this function will close the passed watch.
869
- func (r * Request ) handleWatchList (ctx context.Context , w watch.Interface ) WatchListResult {
889
+ func (r * Request ) handleWatchList (ctx context.Context , w watch.Interface , negotiatedObjectDecoder runtime. Decoder ) WatchListResult {
870
890
defer w .Stop ()
871
891
var lastKey string
872
892
var items []runtime.Object
@@ -900,10 +920,15 @@ func (r *Request) handleWatchList(ctx context.Context, w watch.Interface) WatchL
900
920
lastKey = key
901
921
case watch .Bookmark :
902
922
if meta .GetAnnotations ()[metav1 .InitialEventsAnnotationKey ] == "true" {
923
+ base64EncodedInitialEventsListBlueprint := meta .GetAnnotations ()[metav1 .InitialEventsListBlueprintAnnotationKey ]
924
+ if len (base64EncodedInitialEventsListBlueprint ) == 0 {
925
+ return WatchListResult {err : fmt .Errorf ("%q annotation is missing content" , metav1 .InitialEventsListBlueprintAnnotationKey )}
926
+ }
903
927
return WatchListResult {
904
- items : items ,
905
- initialEventsEndBookmarkRV : meta .GetResourceVersion (),
906
- gv : r .c .content .GroupVersion ,
928
+ items : items ,
929
+ initialEventsEndBookmarkRV : meta .GetResourceVersion (),
930
+ negotiatedObjectDecoder : negotiatedObjectDecoder ,
931
+ base64EncodedInitialEventsListBlueprint : base64EncodedInitialEventsListBlueprint ,
907
932
}
908
933
}
909
934
default :
@@ -913,15 +938,15 @@ func (r *Request) handleWatchList(ctx context.Context, w watch.Interface) WatchL
913
938
}
914
939
}
915
940
916
- func (r * Request ) newStreamWatcher (resp * http.Response ) (watch.Interface , error ) {
941
+ func (r * Request ) newStreamWatcher (resp * http.Response ) (watch.Interface , runtime. Decoder , error ) {
917
942
contentType := resp .Header .Get ("Content-Type" )
918
943
mediaType , params , err := mime .ParseMediaType (contentType )
919
944
if err != nil {
920
945
klog .V (4 ).Infof ("Unexpected content type from the server: %q: %v" , contentType , err )
921
946
}
922
947
objectDecoder , streamingSerializer , framer , err := r .c .content .Negotiator .StreamDecoder (mediaType , params )
923
948
if err != nil {
924
- return nil , err
949
+ return nil , nil , err
925
950
}
926
951
927
952
handleWarnings (resp .Header , r .warningHandler )
@@ -934,7 +959,7 @@ func (r *Request) newStreamWatcher(resp *http.Response) (watch.Interface, error)
934
959
// use 500 to indicate that the cause of the error is unknown - other error codes
935
960
// are more specific to HTTP interactions, and set a reason
936
961
errors .NewClientErrorReporter (http .StatusInternalServerError , r .verb , "ClientWatchDecoding" ),
937
- ), nil
962
+ ), objectDecoder , nil
938
963
}
939
964
940
965
// updateRequestResultMetric increments the RequestResult metric counter,
0 commit comments