client-go discovery包源码解析
2023-04-18 16:47:26 时间
1. 概述
discovery包主要用来发现服务器支持的API组、版本和资源的方法,及服务端支持的swagger api
代码示例:
package main
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/tools/clientcmd"
"log"
)
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "./config")
if err != nil {
log.Fatal(err)
}
discoverClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
log.Fatal(err)
}
_, apiResourceList, err := discoverClient.ServerGroupsAndResources()
for _, v := range apiResourceList {
gv, err := schema.ParseGroupVersion(v.GroupVersion)
if err != nil {
log.Fatal(err)
}
for _, resource := range v.APIResources {
fmt.Println("name:", resource.Name, " ", "group:", gv.Group, " ", "version:", gv.Version)
}
}
}
2. 源码解读
2.1 discovery_client.go
- 定义全局变量:
const (
// defaultRetries指动态获取resource失败,重试的次数(例如CustomResourceDefinitions
defaultRetries = 2
// protobuf mime 类型
openAPIV2mimePb = "application/com.github.proto-openapi.spec.v2@v1.0+protobuf"
// defaultTimeout是在RESTClient上未设置超时时每个请求的最大时间。默认为32秒,以便相对于其他存在的超时具有可区分的时间长度。 defaultTimeout = 32 * time.Second
defaultTimeout = 32 * time.Second
// defaultBurst是发现客户端的令牌桶速率限制的使用的默认burst
defaultBurst = 300
AcceptV1 = runtime.ContentTypeJSON
// 聚合的discovery类型(当前为v2beta1)。注意:目前,我们假设“g”、“v”和“as”的顺序来自服务器。只有当我们能够做出这样的假设时,我们才能比较这个字符串
AcceptV2Beta1 = runtime.ContentTypeJSON + ";" + "g=apidiscovery.k8s.io;v=v2beta1;as=APIGroupDiscoveryList"
// 通过设置descovery的可接受的类型顺序,确定聚合descovery的优先级
acceptDiscoveryFormats = AcceptV2Beta1 + "," + AcceptV1
- 接口:
// DiscoveryInterface 动态发现服务器支持的API groups,versions and resources.
type DiscoveryInterface interface {
RESTClient() restclient.Interface
ServerGroupsInterface
ServerResourcesInterface
ServerVersionInterface
OpenAPISchemaInterface
OpenAPIV3SchemaInterface
// 返回仅接收旧版发现格式的当前发现客户端的副本,如果当前发现客户端不支持仅旧版发现,则返回指向该客户端的指针。
WithLegacy() DiscoveryInterface
}
// AggregatedDiscoveryInterface扩展了DiscoveryInterface,使其包括一个可能返回发现资源和发现组的方法,这就是较新的聚合发现格式(APIGroupDiscoveryList)的作用。
type AggregatedDiscoveryInterface interface {
DiscoveryInterface
GroupsAndMaybeResources() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, error)
}
// CachedDiscoveryInterface 是一个具有缓存失效和刷新的 DiscoveryInterface。
// 注意:如果ServerResourcesForGroupVersion方法返回缓存未命中错误,
// 用户需要显式调用Invalidate清除缓存,否则下次会返回同样的缓存未命中错误。
type CachedDiscoveryInterface interface {
DiscoveryInterface
//如果在缓存未能找到,Fresh 应该告诉调用者是否重试(false = 重试,true = 不需要重试)。
// TODO: this needs to be revisited, this interface can't be locked properly
// and doesn't make a lot of sense.
Fresh() bool
//Invalidate 强制不使用早于当前时间的缓存数据
Invalidate()
}
// ServerGroupsInterface 具有获取 API 服务器上支持的group的方法
type ServerGroupsInterface interface {
// ServerGroups 返回支持的组,包括支持的版本和首选版本等信息。
ServerGroups() (*metav1.APIGroupList, error)
}
// ServerResourcesInterface 具有获取 API 服务器上支持的resource的方法
type ServerResourcesInterface interface {
// ServerResourcesForGroupVersion 返回组和版本支持的资源
ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error)
// ServerGroupsAndResources为所有组和版本返回支持的组和资源。
// 返回的组和资源列表可能是非零的,即使在非零错误的情况下也会有部分结果
ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)
// ServerPreferredResources使用服务器首选的版本返回支持的资源。
// 返回的组和资源列表可能是非零的,即使在非零错误的情况下也会有部分结果。
ServerPreferredResources() ([]*metav1.APIResourceList, error)
// ServerPreferredNamespacedResources使用服务器首选的版本返回受支持的命名空间资源。
// 返回的资源列表可能是非nil,即使在非nil错误的情况下也会有部分结果。
ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error)
}
// ServerVersionInterface 有一个检索服务器版本的方法。
type ServerVersionInterface interface {
// ServerVersion 检索并解析服务器的版本(git 版本)。
ServerVersion() (*version.Info, error)
}
// OpenAPISchemaInterface 有一个方法来检索open API的schema。
type OpenAPISchemaInterface interface {
// OpenAPISchema 检索并解析服务器支持的 swagger API 模式。
OpenAPISchema() (*openapi_v2.Document, error)
}
- Discovery执行内部方法的函数:
// fetchServerResourcesForGroupVersions使用Discovery客户端并行获取指定组的资源。 func fetchGroupVersionResources(d DiscoveryInterface, apiGroups *metav1.APIGroupList) (map[schema.GroupVersion]*metav1.APIResourceList, map[schema.GroupVersion]error) { groupVersionResources := make(map[schema.GroupVersion]*metav1.APIResourceList) failedGroups := make(map[schema.GroupVersion]error) wg := &sync.WaitGroup{} resultLock := &sync.Mutex{} for _, apiGroup := range apiGroups.Groups { // 遍历每个group中的versions for _, version := range apiGroup.Versions { groupVersion := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version} wg.Add(1) // 开启goruntime执行 go func() { defer wg.Done() defer utilruntime.HandleCrash() // 执行DiscoveryClient具体实现的ServerResourcesForGroupVersion apiResourceList, err := d.ServerResourcesForGroupVersion(groupVersion.String()) // lock to record results resultLock.Lock() defer resultLock.Unlock() if err != nil { // TODO: maybe restrict this to NotFound errors failedGroups[groupVersion] = err } if apiResourceList != nil { // 执行DiscoveryClient具体实现的ServerResourcesForGroupVersion groupVersionResources[groupVersion] = apiResourceList } }() } } wg.Wait() return groupVersionResources, failedGroups } func ServerGroupsAndResources(d DiscoveryInterface) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { var sgs *metav1.APIGroupList var resources []*metav1.APIResourceList var err error // 如果传递的discover对象实现了AggregatedDiscoveryInterface,则尝试使用组和资源检索聚合发现。 if ad, ok := d.(AggregatedDiscoveryInterface); ok { var resourcesByGV map[schema.GroupVersion]*metav1.APIResourceList sgs, resourcesByGV, err = ad.GroupsAndMaybeResources() for _, resourceList := range resourcesByGV { resources = append(resources, resourceList) } } else { // 获取apiGroupList sgs, err = d.ServerGroups() } if sgs == nil { return nil, nil, err } // 获取sgs中所有apiGroup的地址 resultGroups := []*metav1.APIGroup{} for i := range sgs.Groups { resultGroups = append(resultGroups, &sgs.Groups[i]) } if resources != nil { return resultGroups, resources, nil } // DiscoveryClient并行获取指定组列表的资源 groupVersionResources, failedGroups := fetchGroupVersionResources(d, sgs) // 按discoveryclient发现的组/版本顺序排列结果 result := []*metav1.APIResourceList{} for _, apiGroup := range sgs.Groups { for _, version := range apiGroup.Versions { gv := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version} if resources, ok := groupVersionResources[gv]; ok { result = append(result, resources) } } } if len(failedGroups) == 0 { return resultGroups, result, nil } return resultGroups, result, &ErrGroupDiscoveryFailed{Groups: failedGroups} } // ServerPreferredResources使用提供的发现接口查找首选资源 func ServerPreferredResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) { var serverGroupList *metav1.APIGroupList var failedGroups map[schema.GroupVersion]error var groupVersionResources map[schema.GroupVersion]*metav1.APIResourceList var err error // 如果传递的Discovery对象实现了的AggregatedDiscoveryInterface,则尝试检索组和资源。 ad, ok := d.(AggregatedDiscoveryInterface) if ok { serverGroupList, groupVersionResources, err = ad.GroupsAndMaybeResources() } else { serverGroupList, err = d.ServerGroups() } if err != nil { return nil, err } // DiscoveryClient并行获取指定组列表的资源 if groupVersionResources == nil { groupVersionResources, failedGroups = fetchGroupVersionResources(d, serverGroupList) } result := []*metav1.APIResourceList{} grVersions := map[schema.GroupResource]string{} // GroupResource 的选定版本 grAPIResources := map[schema.GroupResource]*metav1.APIResource{} // selected APIResource GroupResource 的选定APIResource gvAPIResourceLists := map[schema.GroupVersion]*metav1.APIResourceList{} // 用于稍后分组的 APIResourceList 的蓝图 for _, apiGroup := range serverGroupList.Groups { for _, version := range apiGroup.Versions { groupVersion := schema.GroupVersion{Group: apiGroup.Name, Version: version.Version} // 判断groupVersionResources是否存在 apiResourceList, ok := groupVersionResources[groupVersion] if !ok { continue } // 创建空列表,稍后在另一个循环中填充 emptyAPIResourceList := metav1.APIResourceList{ GroupVersion: version.GroupVersion, } gvAPIResourceLists[groupVersion] = &emptyAPIResourceList result = append(result, &emptyAPIResourceList) // 遍历上面获取apiResourceList的APIResources for i := range apiResourceList.APIResources { apiResource := &apiResourceList.APIResources[i] // 判断apiResource是否包含/,因为如果包含,则是子资源,所以舍弃 if strings.Contains(apiResource.Name, "/") { continue } // 形成gv gv := schema.GroupResource{Group: apiGroup.Name, Resource: apiResource.Name} if _, ok := grAPIResources[gv]; ok && version.Version != apiGroup.PreferredVersion.Version { // only override with preferred version continue } grVersions[gv] = version.Version grAPIResources[gv] = apiResource } } } // 根据 GroupVersion 将选定的 APIResources 分组到 APIResourceLists(地址变量,改变值也是改变了result的value) for groupResource, apiResource := range grAPIResources { version := grVersions[groupResource] groupVersion := schema.GroupVersion{Group: groupResource.Group, Version: version} apiResourceList := gvAPIResourceLists[groupVersion] apiResourceList.APIResources = append(apiResourceList.APIResources, *apiResource) } if len(failedGroups) == 0 { return result, nil } return result, &ErrGroupDiscoveryFailed{Groups: failedGroups} } // ServerPreferredNamespacedResources使用提供的discovery接口查找首选的命名空间资源 func ServerPreferredNamespacedResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) { // 获取全部的PreferredResources all, err := ServerPreferredResources(d) // 调用helper.go的FilteredBy return FilteredBy(ResourcePredicateFunc(func(groupVersion string, r *metav1.APIResource) bool { return r.Namespaced }), all), err } // withRetries会重试给定的恢复函数,以防ServerGroup()返回后服务器支持的组发生更改 func withRetries(maxRetries int, f func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { var result []*metav1.APIResourceList var resultGroups []*metav1.APIGroup var err error for i := 0; i < maxRetries; i++ { resultGroups, result, err = f() if err == nil { return resultGroups, result, nil } if _, ok := err.(*ErrGroupDiscoveryFailed); !ok { return nil, nil, err } } return resultGroups, result, err }
- DiscoveryClient实现了discover server支持的API组、版本和资源的方法
type DiscoveryClient struct { // restClient http.client的封装 restClient restclient.Interface // api path的前缀 LegacyPrefix string // 强制客户端仅请求“未聚合”(旧版)发现。 UseLegacyDiscovery bool } var _ AggregatedDiscoveryInterface = &DiscoveryClient{} // 将metav1.APIVersions转换为metav1.API组。APIVersions由旧版v1使用,因此组将为“”。 // 其实是把入参APIVersions中每项的GroupVersion替换为version func apiVersionsToAPIGroup(apiVersions *metav1.APIVersions) (apiGroup metav1.APIGroup) { groupVersions := []metav1.GroupVersionForDiscovery{} for _, version := range apiVersions.Versions { groupVersion := metav1.GroupVersionForDiscovery{ GroupVersion: version, Version: version, } groupVersions = append(groupVersions, groupVersion) } apiGroup.Versions = groupVersions // 在api中应该只返回一个groupVersion apiGroup.PreferredVersion = groupVersions[0] return } // GroupsAndMaybeResources返回discovery组,以及(如果是新的聚合发现格式)由groupversion索引资源。 // 合并来自api和api的discovery组和资源(聚合或不聚合)。 // 必须首先对旧组进行排序。服务器将以聚合发现格式或遗留格式返回两个端点(api、apis)。 // 为了安全起见,只有当两个端点都返回了资源时,才会返回资源。 func (d *DiscoveryClient) GroupsAndMaybeResources() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, error) { // Legacy group ordered first (there is only one -- core/v1 group). Returned groups must // be non-nil, but it could be empty. Returned resources, apiResources map could be nil. groups, resources, err := d.downloadLegacy() if err != nil { return nil, nil, err } // Discovery groups and (possibly) resources downloaded from /apis. apiGroups, apiResources, aerr := d.downloadAPIs() if aerr != nil { return nil, nil, aerr } // Merge apis groups into the legacy groups. for _, group := range apiGroups.Groups { groups.Groups = append(groups.Groups, group) } // For safety, only return resources if both endpoints returned resources. if resources != nil && apiResources != nil { for gv, resourceList := range apiResources { resources[gv] = resourceList } } else if resources != nil { resources = nil } return groups, resources, err } // downloadLegacy在api处返回旧版v1 GVR的discovery组和可能的资源,如果发生错误,则返回错误。如果服务器返回未聚合的发现, // 则资源映射可能为零。 func (d *DiscoveryClient) downloadLegacy() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, error) { accept := acceptDiscoveryFormats if d.UseLegacyDiscovery { accept = AcceptV1 } var responseContentType string body, err := d.restClient.Get(). AbsPath("/api"). SetHeader("Accept", accept). Do(context.TODO()). ContentType(&responseContentType). Raw() // Special error handling for 403 or 404 to be compatible with older v1.0 servers. // Return empty group list to be merged with /apis. if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) { return nil, nil, err } if err != nil && (errors.IsNotFound(err) || errors.IsForbidden(err)) { return &metav1.APIGroupList{}, nil, nil } apiGroupList := &metav1.APIGroupList{} var resourcesByGV map[schema.GroupVersion]*metav1.APIResourceList // Switch on content-type server responded with: aggregated or unaggregated. switch responseContentType { case AcceptV1: var v metav1.APIVersions err = json.Unmarshal(body, &v) if err != nil { return nil, nil, err } apiGroup := metav1.APIGroup{} if len(v.Versions) != 0 { apiGroup = apiVersionsToAPIGroup(&v) } apiGroupList.Groups = []metav1.APIGroup{apiGroup} case AcceptV2Beta1: var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList err = json.Unmarshal(body, &aggregatedDiscovery) if err != nil { return nil, nil, err } apiGroupList, resourcesByGV = SplitGroupsAndResources(aggregatedDiscovery) default: return nil, nil, fmt.Errorf("Unknown discovery response content-type: %s", responseContentType) } return apiGroupList, resourcesByGV, nil } // downloadAPI返回discovery组和(如果是聚合格式)discovery资源。返回的组将始终存在,但资源映射可能为零。 func (d *DiscoveryClient) downloadAPIs() (*metav1.APIGroupList, map[schema.GroupVersion]*metav1.APIResourceList, error) { accept := acceptDiscoveryFormats if d.UseLegacyDiscovery { accept = AcceptV1 } var responseContentType string body, err := d.restClient.Get(). AbsPath("/apis"). SetHeader("Accept", accept). Do(context.TODO()). ContentType(&responseContentType). Raw() // Special error handling for 403 or 404 to be compatible with older v1.0 servers. // Return empty group list to be merged with /api. if err != nil && !errors.IsNotFound(err) && !errors.IsForbidden(err) { return nil, nil, err } if err != nil && (errors.IsNotFound(err) || errors.IsForbidden(err)) { return &metav1.APIGroupList{}, nil, nil } apiGroupList := &metav1.APIGroupList{} var resourcesByGV map[schema.GroupVersion]*metav1.APIResourceList // Switch on content-type server responded with: aggregated or unaggregated. switch responseContentType { case AcceptV1: err = json.Unmarshal(body, apiGroupList) if err != nil { return nil, nil, err } case AcceptV2Beta1: var aggregatedDiscovery apidiscovery.APIGroupDiscoveryList err = json.Unmarshal(body, &aggregatedDiscovery) if err != nil { return nil, nil, err } apiGroupList, resourcesByGV = SplitGroupsAndResources(aggregatedDiscovery) default: return nil, nil, fmt.Errorf("Unknown discovery response content-type: %s", responseContentType) } return apiGroupList, resourcesByGV, nil } // ServerGroups返回支持的组,其中包含支持的版本和首选版本等信息。 实现ServerGroupsInterface接口 func (d *DiscoveryClient) ServerGroups() (*metav1.APIGroupList, error) { groups, _, err := d.GroupsAndMaybeResources() if err != nil { return nil, err } return groups, nil } // 实现了ServerResourcesInterface的ServerResourcesForGroupVersion方法 注意获取resource在不同版本下的path func (d *DiscoveryClient) ServerResourcesForGroupVersion(groupVersion string) (resources *metav1.APIResourceList, err error) { url := url.URL{} if len(groupVersion) == 0 { return nil, fmt.Errorf("groupVersion shouldn't be empty") } // 这里体现了获取core group的resource的path为/api/v1 // 获取非core group的resource的path为/apis/$GROUP_NAME/$VERSION if len(d.LegacyPrefix) > 0 && groupVersion == "v1" { url.Path = d.LegacyPrefix + "/" + groupVersion } else { url.Path = "/apis/" + groupVersion } resources = &metav1.APIResourceList{ GroupVersion: groupVersion, } err = d.restClient.Get().AbsPath(url.String()).Do(context.TODO()).Into(resources) if err != nil { // ignore 403 or 404 error to be compatible with an v1.0 server. if groupVersion == "v1" && (errors.IsNotFound(err) || errors.IsForbidden(err)) { return resources, nil } return nil, err } return resources, nil } // ServerGroupsAndResources返回所有组和版本支持的资源。 func (d *DiscoveryClient) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { return withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { return ServerGroupsAndResources(d) }) } // ServerPreferredResources使用服务器首选的版本返回支持的资源。 func (d *DiscoveryClient) ServerPreferredResources() ([]*metav1.APIResourceList, error) { _, rs, err := withRetries(defaultRetries, func() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) { // 调用上面函数部分的ServerPreferredResources rs, err := ServerPreferredResources(d) return nil, rs, err }) return rs, err } // ServerPreferredNamespacedResources使用服务器首选的版本返回受支持的命名空间资源。 func (d *DiscoveryClient) ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error) { return ServerPreferredNamespacedResources(d) } // ServerPreferredNamespacedResources使用提供的discovery接口查找首选的命名空间资源 func ServerPreferredNamespacedResources(d DiscoveryInterface) ([]*metav1.APIResourceList, error) { all, err := ServerPreferredResources(d) return FilteredBy(ResourcePredicateFunc(func(groupVersion string, r *metav1.APIResource) bool { return r.Namespaced }), all), err } // ServerVersion检索并解析服务器的版本(git版本)。 func (d *DiscoveryClient) ServerVersion() (*version.Info, error) { // 获取服务端的version 等效于kubectl version body, err := d.restClient.Get().AbsPath("/version").Do(context.TODO()).Raw() if err != nil { return nil, err } var info version.Info err = json.Unmarshal(body, &info) if err != nil { return nil, fmt.Errorf("unable to parse the server version: %v", err) } return &info, nil } // OpenAPISchema使用rest客户端获取openAPIv2模式,并解析proto。 func (d *DiscoveryClient) OpenAPISchema() (*openapi_v2.Document, error) { data, err := d.restClient.Get().AbsPath("/openapi/v2").SetHeader("Accept", openAPIV2mimePb).Do(context.TODO()).Raw() if err != nil { if errors.IsForbidden(err) || errors.IsNotFound(err) || errors.IsNotAcceptable(err) { // 未在旧服务器中找到注册的单个端点,请尝试获取旧端点 // TODO:当kubectlclient不能与1.9服务器一起使用时删除此端点 data, err = d.restClient.Get().AbsPath("/swagger-2.0.0.pb-v1").Do(context.TODO()).Raw() if err != nil { return nil, err } } else { return nil, err } } document := &openapi_v2.Document{} err = proto.Unmarshal(data, document) if err != nil { return nil, err } return document, nil }
// 设置默认属性
func setDiscoveryDefaults(config *restclient.Config) error {
config.APIPath = ""
config.GroupVersion = nil
// 如果Timeout == 0 设置为defaultTimeout
if config.Timeout == 0 {
config.Timeout = defaultTimeout
}
// if a burst limit is not already configured
if config.Burst == 0 {
// discovery预计是突发的,增加默认突发以适应查找许多API组的资源信息。
// 匹配ConfigFlagsToDiscoveryClient()设置的突发。请参阅https://issue.k8s.io86149
config.Burst = defaultBurst
}
// 用来编解码
codec := runtime.NoopEncoder{Decoder: scheme.Codecs.UniversalDecoder()}
// 生成并设置序列化,用来序列化(输入)和反序列化(输出)
config.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec})
if len(config.UserAgent) == 0 {
config.UserAgent = restclient.DefaultKubernetesUserAgent()
}
return nil
}
// NewDiscoveryClientForConfig 为给定的配置创建一个新的 DiscoveryClient。此客户端可用于发现 API 服务器中支持的资源。
func NewDiscoveryClientForConfig(c *restclient.Config) (*DiscoveryClient, error) {
config := *c
if err := setDiscoveryDefaults(&config); err != nil {
return nil, err
}
// 调用rest包下的client.go中的UnversionedRESTClientFor生成restclient
httpClient, err := restclient.HTTPClientFor(&config)
if err != nil {
return nil, err
}
return NewDiscoveryClientForConfigAndClient(&config, httpClient)
}
// 类似于NewDiscoveryClientForConfig ,区别是此方法如果出现err,则panic
func NewDiscoveryClientForConfigOrDie(c *restclient.Config) *DiscoveryClient {
client, err := NewDiscoveryClientForConfig(c)
if err != nil {
panic(err)
}
return client
}
2.2 helper.go
接口及其结构体:
// ResourcePredicate 有一个方法来检查资源是否匹配给定条件
type ResourcePredicate interface {
Match(groupVersion string, r *metav1.APIResource) bool
}
// 如果ResourcePredicateFunc与基于自定义条件的资源匹配,则返回true。.
type ResourcePredicateFunc func(groupVersion string, r *metav1.APIResource) bool
// Match 是 ResourcePredicateFunc 的包装器.
func (fn ResourcePredicateFunc) Match(groupVersion string, r *metav1.APIResource) bool {
return fn(groupVersion, r)
}
// supportsAllVerbs 是一个匹配资源的谓词,如果所有给定的动词都被支持,则匹配
type SupportsAllVerbs struct {
Verbs []string
}
// 匹配检查资源是否包含所有给定的动词。
func (p SupportsAllVerbs) Match(groupVersion string, r *metav1.APIResource) bool {
return sets.NewString([]string(r.Verbs)...).HasAll(p.Verbs...)
}
函数
// MatchesServerVersion 查询服务器以将客户端的构建版本(git hash) 与服务器的构建版本进行比较。如果无法链接服务器或版本不完全匹配,则返回错误。
func MatchesServerVersion(clientVersion apimachineryversion.Info, client DiscoveryInterface) error {
sVer, err := client.ServerVersion()
if err != nil {
return fmt.Errorf("couldn't read version from server: %v", err)
}
// GitVersion includes GitCommit and GitTreeState, but best to be safe?
if clientVersion.GitVersion != sVer.GitVersion || clientVersion.GitCommit != sVer.GitCommit || clientVersion.GitTreeState != sVer.GitTreeState {
return fmt.Errorf("server version (%#v) differs from client version (%#v)", sVer, clientVersion)
}
return nil
}
// 如果服务器没有所需的版本,ServerSupportsVersion 将返回错误
func ServerSupportsVersion(client DiscoveryInterface, requiredGV schema.GroupVersion) error {
// 获取所有的groups
groups, err := client.ServerGroups()
if err != nil {
// 几乎总是一个连接错误
return err
}
// 提取groups中每项的groupversion
versions := metav1.ExtractGroupVersions(groups)
// 去重
serverVersions := sets.String{}
for _, v := range versions {
serverVersions.Insert(v)
}
// 判断是否包含
if serverVersions.Has(requiredGV.String()) {
return nil
}
// 如果serverVersions的没有元素,那么可能是403 Forbidden errors
if len(serverVersions) == 0 {
return nil
}
return fmt.Errorf("server does not support API version %q", requiredGV)
}
// GroupVersionResources 将 APIResourceLists 转换为 GroupVersionResources,并作为map的key形成map返回。
func GroupVersionResources(rls []*metav1.APIResourceList) (map[schema.GroupVersionResource]struct{}, error) {
gvrs := map[schema.GroupVersionResource]struct{}{}
// 遍历
for _, rl := range rls {
// str的gv转化为GroupVersion
gv, err := schema.ParseGroupVersion(rl.GroupVersion)
if err != nil {
return nil, err
}
// 遍历APIResources
for i := range rl.APIResources {
// 形成gvr并插入map中
gvrs[schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: rl.APIResources[i].Name}] = struct{}{}
}
}
return gvrs, nil
}
// FilteredBy 按给定ResourcePredicate过滤
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)
}
}
return result
}
相关文章
- 大数据框架Hadoop篇之Hadoop入门
- 【UML】UML建模
- 【算法】最短路算法
- mac pro M1(ARM)安装:安装zookeeper可视化工具PrettyZoo、ZooKeeperAssistant
- 2022年甘肃省职业院校技能大赛“网络搭建与应用”赛项
- 电子科技大学人工智能期末复习笔记(一):搜索问题
- springboot 集成webSocket
- 数据结构-带头双向循环链表
- Linux Centos 7 压缩和解压缩命令
- Monorepo,大型前端项目管理模式实践
- 使用docker启动rabbitmq
- acwing 第63场周赛【2022.08.06】
- Rocket MQ报错No route info of this topic的问题探究
- 数据结构与算法(二):线性表
- 抱抱脸:ChatGPT背后的算法——RLHF | 附12篇RLHF必刷论文
- 100种目标检测数据集【voc格式yolo格式json格式coco格式】+YOLO系列算法源码及训练好的模型
- 秒懂算法 | 基于朴素贝叶斯算法的垃圾信息的识别
- 【MySQL】MySQL表的增删改查(CRUD)
- 【目标检测】雷达目标CFAR检测算法
- 最小生成树——Prim算法(详细图解)