k8s.io/client-go@v0.31.1/util/watchlist/watch_list.go (about)

     1  /*
     2  Copyright 2024 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package watchlist
    18  
    19  import (
    20  	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
    21  	metainternalversionvalidation "k8s.io/apimachinery/pkg/apis/meta/internalversion/validation"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/runtime"
    24  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    25  	clientfeatures "k8s.io/client-go/features"
    26  	"k8s.io/utils/ptr"
    27  )
    28  
    29  var scheme = runtime.NewScheme()
    30  
    31  func init() {
    32  	utilruntime.Must(metainternalversion.AddToScheme(scheme))
    33  }
    34  
    35  // PrepareWatchListOptionsFromListOptions creates a new ListOptions
    36  // that can be used for a watch-list request from the given listOptions.
    37  //
    38  // This function also determines if the given listOptions can be used to form a watch-list request,
    39  // which would result in streaming semantically equivalent data from the server.
    40  func PrepareWatchListOptionsFromListOptions(listOptions metav1.ListOptions) (metav1.ListOptions, bool, error) {
    41  	if !clientfeatures.FeatureGates().Enabled(clientfeatures.WatchListClient) {
    42  		return metav1.ListOptions{}, false, nil
    43  	}
    44  
    45  	internalListOptions := &metainternalversion.ListOptions{}
    46  	if err := scheme.Convert(&listOptions, internalListOptions, nil); err != nil {
    47  		return metav1.ListOptions{}, false, err
    48  	}
    49  	if errs := metainternalversionvalidation.ValidateListOptions(internalListOptions, true); len(errs) > 0 {
    50  		return metav1.ListOptions{}, false, nil
    51  	}
    52  
    53  	watchListOptions := listOptions
    54  	// this is our legacy case, the cache ignores LIMIT for
    55  	// ResourceVersion == 0 and RVM=unset|NotOlderThan
    56  	if listOptions.Limit > 0 && listOptions.ResourceVersion != "0" {
    57  		return metav1.ListOptions{}, false, nil
    58  	}
    59  	watchListOptions.Limit = 0
    60  
    61  	// to ensure that we can create a watch-list request that returns
    62  	// semantically equivalent data for the given listOptions,
    63  	// we need to validate that the RVM for the list is supported by watch-list requests.
    64  	if listOptions.ResourceVersionMatch == metav1.ResourceVersionMatchExact {
    65  		return metav1.ListOptions{}, false, nil
    66  	}
    67  	watchListOptions.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
    68  
    69  	watchListOptions.Watch = true
    70  	watchListOptions.AllowWatchBookmarks = true
    71  	watchListOptions.SendInitialEvents = ptr.To(true)
    72  
    73  	internalWatchListOptions := &metainternalversion.ListOptions{}
    74  	if err := scheme.Convert(&watchListOptions, internalWatchListOptions, nil); err != nil {
    75  		return metav1.ListOptions{}, false, err
    76  	}
    77  	if errs := metainternalversionvalidation.ValidateListOptions(internalWatchListOptions, true); len(errs) > 0 {
    78  		return metav1.ListOptions{}, false, nil
    79  	}
    80  
    81  	return watchListOptions, true, nil
    82  }