k8s.io/client-go@v0.31.1/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 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 }