helper.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package discovery
  14. import (
  15. "fmt"
  16. apierrors "k8s.io/apimachinery/pkg/api/errors"
  17. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  18. "k8s.io/apimachinery/pkg/runtime/schema"
  19. "k8s.io/apimachinery/pkg/util/sets"
  20. apimachineryversion "k8s.io/apimachinery/pkg/version"
  21. )
  22. // IsResourceEnabled queries the server to determine if the resource specified is present on the server.
  23. // This is particularly helpful when writing a controller or an e2e test that requires a particular resource to function.
  24. func IsResourceEnabled(client DiscoveryInterface, resourceToCheck schema.GroupVersionResource) (bool, error) {
  25. // this is a single request. The ServerResourcesForGroupVersion handles the core v1 group as legacy.
  26. resourceList, err := client.ServerResourcesForGroupVersion(resourceToCheck.GroupVersion().String())
  27. if apierrors.IsNotFound(err) { // if the discovery endpoint isn't present, then the resource isn't present.
  28. return false, nil
  29. }
  30. if err != nil {
  31. return false, err
  32. }
  33. for _, actualResource := range resourceList.APIResources {
  34. if actualResource.Name == resourceToCheck.Resource {
  35. return true, nil
  36. }
  37. }
  38. return false, nil
  39. }
  40. // MatchesServerVersion queries the server to compares the build version
  41. // (git hash) of the client with the server's build version. It returns an error
  42. // if it failed to contact the server or if the versions are not an exact match.
  43. func MatchesServerVersion(clientVersion apimachineryversion.Info, client DiscoveryInterface) error {
  44. sVer, err := client.ServerVersion()
  45. if err != nil {
  46. return fmt.Errorf("couldn't read version from server: %v", err)
  47. }
  48. // GitVersion includes GitCommit and GitTreeState, but best to be safe?
  49. if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit || clientVersion.GitTreeState != sVer.GitTreeState {
  50. return fmt.Errorf("server version (%#v) differs from client version (%#v)", sVer, clientVersion)
  51. }
  52. return nil
  53. }
  54. // ServerSupportsVersion returns an error if the server doesn't have the required version
  55. func ServerSupportsVersion(client DiscoveryInterface, requiredGV schema.GroupVersion) error {
  56. groups, err := client.ServerGroups()
  57. if err != nil {
  58. // This is almost always a connection error, and higher level code should treat this as a generic error,
  59. // not a negotiation specific error.
  60. return err
  61. }
  62. versions := metav1.ExtractGroupVersions(groups)
  63. serverVersions := sets.String{}
  64. for _, v := range versions {
  65. serverVersions.Insert(v)
  66. }
  67. if serverVersions.Has(requiredGV.String()) {
  68. return nil
  69. }
  70. // If the server supports no versions, then we should pretend it has the version because of old servers.
  71. // This can happen because discovery fails due to 403 Forbidden errors
  72. if len(serverVersions) == 0 {
  73. return nil
  74. }
  75. return fmt.Errorf("server does not support API version %q", requiredGV)
  76. }
  77. // GroupVersionResources converts APIResourceLists to the GroupVersionResources.
  78. func GroupVersionResources(rls []*metav1.APIResourceList) (map[schema.GroupVersionResource]struct{}, error) {
  79. gvrs := map[schema.GroupVersionResource]struct{}{}
  80. for _, rl := range rls {
  81. gv, err := schema.ParseGroupVersion(rl.GroupVersion)
  82. if err != nil {
  83. return nil, err
  84. }
  85. for i := range rl.APIResources {
  86. gvrs[schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: rl.APIResources[i].Name}] = struct{}{}
  87. }
  88. }
  89. return gvrs, nil
  90. }
  91. // FilteredBy filters by the given predicate. Empty APIResourceLists are dropped.
  92. func FilteredBy(pred ResourcePredicate, rls []*metav1.APIResourceList) []*metav1.APIResourceList {
  93. result := []*metav1.APIResourceList{}
  94. for _, rl := range rls {
  95. filtered := *rl
  96. filtered.APIResources = nil
  97. for i := range rl.APIResources {
  98. if pred.Match(rl.GroupVersion, &rl.APIResources[i]) {
  99. filtered.APIResources = append(filtered.APIResources, rl.APIResources[i])
  100. }
  101. }
  102. if filtered.APIResources != nil {
  103. result = append(result, &filtered)
  104. }
  105. }
  106. return result
  107. }
  108. // ResourcePredicate has a method to check if a resource matches a given condition.
  109. type ResourcePredicate interface {
  110. Match(groupVersion string, r *metav1.APIResource) bool
  111. }
  112. // ResourcePredicateFunc returns true if it matches a resource based on a custom condition.
  113. type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool
  114. // Match is a wrapper around ResourcePredicateFunc.
  115. func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool {
  116. return fn(groupVersion, r)
  117. }
  118. // SupportsAllVerbs is a predicate matching a resource iff all given verbs are supported.
  119. type SupportsAllVerbs struct {
  120. Verbs []string
  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. }