k8s.io/client-go@v0.22.2/discovery/helper.go (about)

     1  /*
     2  Copyright 2016 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 discovery
    18  
    19  import (
    20  	"fmt"
    21  
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	"k8s.io/apimachinery/pkg/util/sets"
    25  	apimachineryversion "k8s.io/apimachinery/pkg/version"
    26  )
    27  
    28  // MatchesServerVersion queries the server to compares the build version
    29  // (git hash) of the client with the server's build version. It returns an error
    30  // if it failed to contact the server or if the versions are not an exact match.
    31  func MatchesServerVersion(clientVersion apimachineryversion.Info, client DiscoveryInterface) error {
    32  	sVer, err := client.ServerVersion()
    33  	if err != nil {
    34  		return fmt.Errorf("couldn't read version from server: %v", err)
    35  	}
    36  	// GitVersion includes GitCommit and GitTreeState, but best to be safe?
    37  	if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit || clientVersion.GitTreeState != sVer.GitTreeState {
    38  		return fmt.Errorf("server version (%#v) differs from client version (%#v)", sVer, clientVersion)
    39  	}
    40  
    41  	return nil
    42  }
    43  
    44  // ServerSupportsVersion returns an error if the server doesn't have the required version
    45  func ServerSupportsVersion(client DiscoveryInterface, requiredGV schema.GroupVersion) error {
    46  	groups, err := client.ServerGroups()
    47  	if err != nil {
    48  		// This is almost always a connection error, and higher level code should treat this as a generic error,
    49  		// not a negotiation specific error.
    50  		return err
    51  	}
    52  	versions := metav1.ExtractGroupVersions(groups)
    53  	serverVersions := sets.String{}
    54  	for _, v := range versions {
    55  		serverVersions.Insert(v)
    56  	}
    57  
    58  	if serverVersions.Has(requiredGV.String()) {
    59  		return nil
    60  	}
    61  
    62  	// If the server supports no versions, then we should pretend it has the version because of old servers.
    63  	// This can happen because discovery fails due to 403 Forbidden errors
    64  	if len(serverVersions) == 0 {
    65  		return nil
    66  	}
    67  
    68  	return fmt.Errorf("server does not support API version %q", requiredGV)
    69  }
    70  
    71  // GroupVersionResources converts APIResourceLists to the GroupVersionResources.
    72  func GroupVersionResources(rls []*metav1.APIResourceList) (map[schema.GroupVersionResource]struct{}, error) {
    73  	gvrs := map[schema.GroupVersionResource]struct{}{}
    74  	for _, rl := range rls {
    75  		gv, err := schema.ParseGroupVersion(rl.GroupVersion)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		for i := range rl.APIResources {
    80  			gvrs[schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: rl.APIResources[i].Name}] = struct{}{}
    81  		}
    82  	}
    83  	return gvrs, nil
    84  }
    85  
    86  // FilteredBy filters by the given predicate. Empty APIResourceLists are dropped.
    87  func FilteredBy(pred ResourcePredicate, rls []*metav1.APIResourceList) []*metav1.APIResourceList {
    88  	result := []*metav1.APIResourceList{}
    89  	for _, rl := range rls {
    90  		filtered := *rl
    91  		filtered.APIResources = nil
    92  		for i := range rl.APIResources {
    93  			if pred.Match(rl.GroupVersion, &rl.APIResources[i]) {
    94  				filtered.APIResources = append(filtered.APIResources, rl.APIResources[i])
    95  			}
    96  		}
    97  		if filtered.APIResources != nil {
    98  			result = append(result, &filtered)
    99  		}
   100  	}
   101  	return result
   102  }
   103  
   104  // ResourcePredicate has a method to check if a resource matches a given condition.
   105  type ResourcePredicate interface {
   106  	Match(groupVersion string, r *metav1.APIResource) bool
   107  }
   108  
   109  // ResourcePredicateFunc returns true if it matches a resource based on a custom condition.
   110  type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool
   111  
   112  // Match is a wrapper around ResourcePredicateFunc.
   113  func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool {
   114  	return fn(groupVersion, r)
   115  }
   116  
   117  // SupportsAllVerbs is a predicate matching a resource iff all given verbs are supported.
   118  type SupportsAllVerbs struct {
   119  	Verbs []string
   120  }
   121  
   122  // Match checks if a resource contains all the given verbs.
   123  func (p SupportsAllVerbs) Match(groupVersion string, r *metav1.APIResource) bool {
   124  	return sets.NewString([]string(r.Verbs)...).HasAll(p.Verbs...)
   125  }