blob: 353d34b3c5ac58f9a74814a6f64ffe8ff7882811 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001/*
2Copyright 2016 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package discovery
18
19import (
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.
31func 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\n", 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)!\n", sVer, clientVersion)
39 }
40
41 return nil
42}
43
44// ServerSupportsVersion returns an error if the server doesn't have the required version
45func 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.
72func 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.
87func 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
104type ResourcePredicate interface {
105 Match(groupVersion string, r *metav1.APIResource) bool
106}
107
108type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool
109
110func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool {
111 return fn(groupVersion, r)
112}
113
114// SupportsAllVerbs is a predicate matching a resource iff all given verbs are supported.
115type SupportsAllVerbs struct {
116 Verbs []string
117}
118
119func (p SupportsAllVerbs) Match(groupVersion string, r *metav1.APIResource) bool {
120 return sets.NewString([]string(r.Verbs)...).HasAll(p.Verbs...)
121}