2017-04-07 13:49:53 +03:00
/ *
Copyright 2016 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package discovery
import (
"fmt"
2018-02-14 11:56:04 +03:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
apimachineryversion "k8s.io/apimachinery/pkg/version"
2017-04-07 13:49:53 +03:00
)
// MatchesServerVersion queries the server to compares the build version
// (git hash) of the client with the server's build version. It returns an error
// if it failed to contact the server or if the versions are not an exact match.
2018-02-14 11:56:04 +03:00
func MatchesServerVersion ( clientVersion apimachineryversion . Info , client DiscoveryInterface ) error {
2017-04-07 13:49:53 +03:00
sVer , err := client . ServerVersion ( )
if err != nil {
return fmt . Errorf ( "couldn't read version from server: %v\n" , err )
}
// GitVersion includes GitCommit and GitTreeState, but best to be safe?
2018-02-14 11:56:04 +03:00
if clientVersion . GitVersion != sVer . GitVersion || clientVersion . GitCommit != sVer . GitCommit || clientVersion . GitTreeState != sVer . GitTreeState {
return fmt . Errorf ( "server version (%#v) differs from client version (%#v)!\n" , sVer , clientVersion )
2017-04-07 13:49:53 +03:00
}
return nil
}
2018-02-14 11:56:04 +03:00
// ServerSupportsVersion returns an error if the server doesn't have the required version
func ServerSupportsVersion ( client DiscoveryInterface , requiredGV schema . GroupVersion ) error {
2017-04-07 13:49:53 +03:00
groups , err := client . ServerGroups ( )
if err != nil {
// This is almost always a connection error, and higher level code should treat this as a generic error,
// not a negotiation specific error.
2018-02-14 11:56:04 +03:00
return err
2017-04-07 13:49:53 +03:00
}
2018-02-14 11:56:04 +03:00
versions := metav1 . ExtractGroupVersions ( groups )
2017-04-07 13:49:53 +03:00
serverVersions := sets . String { }
for _ , v := range versions {
serverVersions . Insert ( v )
}
2018-02-14 11:56:04 +03:00
if serverVersions . Has ( requiredGV . String ( ) ) {
return nil
}
2017-04-07 13:49:53 +03:00
2018-02-14 11:56:04 +03:00
// If the server supports no versions, then we should pretend it has the version because of old servers.
// This can happen because discovery fails due to 403 Forbidden errors
if len ( serverVersions ) == 0 {
return nil
}
return fmt . Errorf ( "server does not support API version %q" , requiredGV )
}
// GroupVersionResources converts APIResourceLists to the GroupVersionResources.
func GroupVersionResources ( rls [ ] * metav1 . APIResourceList ) ( map [ schema . GroupVersionResource ] struct { } , error ) {
gvrs := map [ schema . GroupVersionResource ] struct { } { }
for _ , rl := range rls {
gv , err := schema . ParseGroupVersion ( rl . GroupVersion )
if err != nil {
return nil , err
2017-04-07 13:49:53 +03:00
}
2018-02-14 11:56:04 +03:00
for i := range rl . APIResources {
gvrs [ schema . GroupVersionResource { Group : gv . Group , Version : gv . Version , Resource : rl . APIResources [ i ] . Name } ] = struct { } { }
2017-04-07 13:49:53 +03:00
}
}
2018-02-14 11:56:04 +03:00
return gvrs , nil
}
2017-04-07 13:49:53 +03:00
2018-02-14 11:56:04 +03:00
// FilteredBy filters by the given predicate. Empty APIResourceLists are dropped.
func FilteredBy ( pred ResourcePredicate , rls [ ] * metav1 . APIResourceList ) [ ] * metav1 . APIResourceList {
result := [ ] * metav1 . APIResourceList { }
for _ , rl := range rls {
filtered := * rl
filtered . APIResources = nil
for i := range rl . APIResources {
if pred . Match ( rl . GroupVersion , & rl . APIResources [ i ] ) {
filtered . APIResources = append ( filtered . APIResources , rl . APIResources [ i ] )
}
}
if filtered . APIResources != nil {
result = append ( result , & filtered )
2017-04-07 13:49:53 +03:00
}
}
2018-02-14 11:56:04 +03:00
return result
}
2017-04-07 13:49:53 +03:00
2018-02-14 11:56:04 +03:00
type ResourcePredicate interface {
Match ( groupVersion string , r * metav1 . APIResource ) bool
}
type ResourcePredicateFunc func ( groupVersion string , r * metav1 . APIResource ) bool
func ( fn ResourcePredicateFunc ) Match ( groupVersion string , r * metav1 . APIResource ) bool {
return fn ( groupVersion , r )
}
// SupportsAllVerbs is a predicate matching a resource iff all given verbs are supported.
type SupportsAllVerbs struct {
Verbs [ ] string
}
2017-04-07 13:49:53 +03:00
2018-02-14 11:56:04 +03:00
func ( p SupportsAllVerbs ) Match ( groupVersion string , r * metav1 . APIResource ) bool {
return sets . NewString ( [ ] string ( r . Verbs ) ... ) . HasAll ( p . Verbs ... )
2017-04-07 13:49:53 +03:00
}