...

Source file src/k8s.io/client-go/discovery/helper.go

Documentation: k8s.io/client-go/discovery

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

View as plain text