scale包源码分析
scale包
获取实现了RESTMapper(通过discovery client操作apiserver,通过gvr获取gvr/gvk,或者获取RESTMapping)接口的结构体
util.go
- 接口
// PreferredResourceMapper 确定要scale资源的首选版本 type PreferredResourceMapper interface { // ResourceFor 获取部分资源并返回其中首选资源。 ResourceFor(resource schema.GroupVersionResource) (preferredResource schema.GroupVersionResource, err error) } // ScaleKindResolver 知道资源与其scale子资源的GroupVersionKind 之间的关系. type ScaleKindResolver interface { // ScaleForResource 返回给定 GroupVersionResource 的scale子资源的 GroupVersionKind。 ScaleForResource(resource schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) }
- 函数
// NewDiscoveryScaleKindResolver 创建一个新的 ScaleKindResolver,使用参数disovery client来为不同的资源解析正确的 Scale GroupVersionKind。 func NewDiscoveryScaleKindResolver(client discovery.ServerResourcesInterface) ScaleKindResolver { // 构建代理ScaleKindResolver base := &discoveryScaleResolver{ discoveryClient: client, } // 初始化cachedScaleKindResolver return &cachedScaleKindResolver{ base: base, cache: make(map[schema.GroupVersionResource]schema.GroupVersionKind), } } // NewScaleConverter 创建一个新的 ScaleConverter,用于在autoscaling/v1 和 extensions/v1beta1 和apps/(v1/v1beta1/v1beta2)之间进行转换 Scales。 func NewScaleConverter() *ScaleConverter { // k8s/apimachine中的scheme scheme := runtime.NewScheme() // 添加autoscaling/v1 和 extensions/v1beta1 和apps/(v1/v1beta1/v1beta2)到scheme utilruntime.Must(scaleautoscaling.AddToScheme(scheme)) utilruntime.Must(scalescheme.AddToScheme(scheme)) utilruntime.Must(scaleext.AddToScheme(scheme)) utilruntime.Must(scaleextint.AddToScheme(scheme)) utilruntime.Must(scaleappsint.AddToScheme(scheme)) utilruntime.Must(scaleappsv1beta1.AddToScheme(scheme)) utilruntime.Must(scaleappsv1beta2.AddToScheme(scheme)) // 初始化ScaleConverter return &ScaleConverter{ scheme: scheme, codecs: serializer.NewCodecFactory(scheme), internalVersioner: runtime.NewMultiGroupVersioner( scalescheme.SchemeGroupVersion, schema.GroupKind{Group: scaleext.GroupName, Kind: "Scale"}, schema.GroupKind{Group: scaleautoscaling.GroupName, Kind: "Scale"}, // 其实下面这两个这需要存在一个即可,因为在使用MultiGroupVersioner的KindForGroupVersionKinds获取target gvk时,判断的是group和kind,这两个的group和kind都一样 schema.GroupKind{Group: scaleappsv1beta1.GroupName, Kind: "Scale"}, schema.GroupKind{Group: scaleappsv1beta2.GroupName, Kind: "Scale"}, ), } }
- 结构体
// discoveryScaleResolver 是一个 ScaleKindResolver,它使用 DiscoveryInterface 将资源与其scale子资源类型相关联 type discoveryScaleResolver struct { discoveryClient discovery.ServerResourcesInterface } func (r *discoveryScaleResolver) ScaleForResource(inputRes schema.GroupVersionResource) (scaleVersion schema.GroupVersionKind, err error) { // 通过discovery client获取gv下的resources groupVerResources, err := r.discoveryClient.ServerResourcesForGroupVersion(inputRes.GroupVersion().String()) if err != nil { return schema.GroupVersionKind{}, fmt.Errorf("unable to fetch discovery information for %s: %v", inputRes.String(), err) } // 遍历resources for _, resource := range groupVerResources.APIResources { // 把resource 的name按照匹配的"/"切分成两部分 resourceParts := strings.SplitN(resource.Name, "/", 2) // 如果resourceParts的长度不为2或者resourceParts的首个str不等于参数gvr的Resource或者resourceParts的第二个str不等于scale if len(resourceParts) != 2 || resourceParts[0] != inputRes.Resource || resourceParts[1] != "scale" { // 跳过非scale资源,或不是我们寻找的scale资源 continue } scaleGV := inputRes.GroupVersion() // 如果从apiserver获取的resrouces的item的Group和Version都不为空 if resource.Group != "" && resource.Version != "" { // 替换参数gvr的gv scaleGV = schema.GroupVersion{ Group: resource.Group, Version: resource.Version, } } // 以item的Kind和gv获取gvk return scaleGV.WithKind(resource.Kind), nil } // 遍历完成后 未找到,则返回err return schema.GroupVersionKind{}, fmt.Errorf("could not find scale subresource for %s in discovery information", inputRes.String()) } // cachedScaleKindResolver 是一个 ScaleKindResolver,它缓存来自另一个 ScaleKindResolver 的结果,在缓存未命中时重新获取。 type cachedScaleKindResolver struct { // 代理 base ScaleKindResolver // 缓存map key:gvr value: gvk cache map[schema.GroupVersionResource]schema.GroupVersionKind mu sync.RWMutex } func (r *cachedScaleKindResolver) ScaleForResource(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { r.mu.RLock() // 获取缓存中的gvk gvk, isCached := r.cache[resource] r.mu.RUnlock() // 如果缓存中存在,则返回 if isCached { return gvk, nil } // 使用内部代理ScaleKindResolver获取 gvk, err := r.base.ScaleForResource(resource) if err != nil { return schema.GroupVersionKind{}, err } r.mu.Lock() defer r.mu.Unlock() // 插入map缓存中 r.cache[resource] = gvk return gvk, nil } // ScaleConverter 知道如何在外部scale版本之间进行转换。 type ScaleConverter struct { // scheme识别type和gvk scheme *runtime.Scheme // 序列化和反序列化obj codecs serializer.CodecFactory // 其中方法KindForGroupVersionKinds用来获取所需要的gvk internalVersioner runtime.GroupVersioner } // ConvertToVersion 将给定的 *external* 输入对象转换为给定的输出 *external* 输出组版本。(方法内部用ScaleConverter中的internalVersioner做中转) func (c *ScaleConverter) ConvertToVersion(in runtime.Object, outVersion schema.GroupVersion) (runtime.Object, error) { // 使用ScaleConverter中的scheme,以参数in和ScaleConverter的internalVersioner为参数,调用ConvertToVersion来获取内部可以识别的gvk对应的obj scaleInt, err := c.scheme.ConvertToVersion(in, c.internalVersioner) if err != nil { return nil, err } // 用上面获取的内部gvk的obj和参数outVersion,来实现转化 return c.scheme.ConvertToVersion(scaleInt, outVersion) }
interface.go
- 接口
// ScalesGetter 可以产生一个 ScaleInterface(用于操作scale在apiserver ) type ScalesGetter interface { // Scales 为特定的命名空间生成一个 ScaleInterface. // 对于非命名空间资源,将命名空间设置为空字符串。 Scales(namespace string) ScaleInterface } // ScaleInterface 可以获取和更新特定命名空间的资源中实现 scale 子资源的资源的比例. type ScaleInterface interface { // Get 获取给定的可扩展资源的scale信息。 Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscalingapi.Scale, error) // Update 更新给定的可扩展资源的scale信息。 Update(ctx context.Context, resource schema.GroupResource, scale *autoscalingapi.Scale, opts metav1.UpdateOptions) (*autoscalingapi.Scale, error) // Patch patch给定的可扩展资源的scale信息。 Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions) (*autoscalingapi.Scale, error) }
client.go
- 定义变量
// 本文中util.go的函数中有做分析,用来获取scale子资源的外部给定版本和内部可使用版本转化 var scaleConverter = NewScaleConverter() // 根据scheme获取编解码器 var codecs = serializer.NewCodecFactory(scaleConverter.Scheme()) // 创建新的scheme,用来处理请求参数 var parameterScheme = runtime.NewScheme() // 请求参数的编解码器 var dynamicParameterCodec = runtime.NewParameterCodec(parameterScheme) // 定义core api gv var versionV1 = schema.GroupVersion{Version: "v1"} // 初始化函数,用来初始化必要的gvk(WatchEvent和UnversionedTypes)到parameterScheme func init() { metav1.AddToGroupVersion(parameterScheme, versionV1) }
- 函数
// NewForConfig 创建一个新的 ScalesGetter,它使用给定的 RESTMapper将kind解析为resource,并使用给定的 dynamic.APIPathResolverFunc 解析 API 路径。 func NewForConfig(cfg *restclient.Config, mapper PreferredResourceMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) (ScalesGetter, error) { // 避免RESTClientFor 出现异常(因为其内部验证了GroupVersion不能为空) cfg.GroupVersion = &schema.GroupVersion{} // 用来解析request的content type中的支持的media types cfg.NegotiatedSerializer = codecs.WithoutConversion() if len(cfg.UserAgent) == 0 { cfg.UserAgent = restclient.DefaultKubernetesUserAgent() } // 构建rest client client, err := restclient.RESTClientFor(cfg) if err != nil { return nil, err } // 调用New创建ScalesGetter return New(client, mapper, resolver, scaleKindResolver), nil } // New 使用给定的rest client创建一个新的 ScalesGetter 来发出请求. // 客户端上的 GroupVersion 被忽略。 func New(baseClient restclient.Interface, mapper PreferredResourceMapper, resolver dynamic.APIPathResolverFunc, scaleKindResolver ScaleKindResolver) ScalesGetter { return &scaleClient{ mapper: mapper, apiPathResolverFunc: resolver, scaleKindResolver: scaleKindResolver, clientBase: baseClient, } } // convertToScale 将响应体转换为 autoscaling/v1.Scale func convertToScale(result *restclient.Result) (*autoscaling.Scale, error) { // 获取result中的原始数据 scaleBytes, err := result.Raw() if err != nil { return nil, err } // 获取不进行类型转换的解码器 decoder := scaleConverter.codecs.UniversalDecoder(scaleConverter.ScaleVersions()...) // 解码响应result中的原始数据 rawScaleObj, err := runtime.Decode(decoder, scaleBytes) if err != nil { return nil, err } // 无论任何内容都转换为特定的gvk autoscaling/v1.Scale scaleObj, err := scaleConverter.ConvertToVersion(rawScaleObj, autoscaling.SchemeGroupVersion) if err != nil { return nil, fmt.Errorf("received an object from a /scale endpoint which was not convertible to autoscaling Scale: %v", err) } return scaleObj.(*autoscaling.Scale), nil }
- 结构体
// scaleClient 是 ScalesGetter 的实现,它使用 RESTMapper 和通用 REST 客户端来支持可发现的资源. // 它的行为与动态 ClientPool 有点相似,但更具体地适用于 Scale. type scaleClient struct { mapper PreferredResourceMapper apiPathResolverFunc dynamic.APIPathResolverFunc scaleKindResolver ScaleKindResolver clientBase restclient.Interface } // apiPathFor 返回给定 GroupVersion 的绝对 api 路径 func (c *scaleClient) apiPathFor(groupVer schema.GroupVersion) string { // 我们需要根据 GroupVersion 设置 API 路径(如果没有设置,则默认为旧路径) apiPath := c.apiPathResolverFunc(groupVer.WithKind("")) if apiPath == "" { apiPath = "/api" } // 在apiPath后添加gv信息 return restclient.DefaultVersionedAPIPath(apiPath, groupVer) } // pathAndVersionFor 为给定的 GroupResource 返回适当的基本路径和关联的完整 GroupVersionResource func (c *scaleClient) pathAndVersionFor(resource schema.GroupResource) (string, schema.GroupVersionResource, error) { // 通过gvr获取mapper中符合条件的首选gvr gvr, err := c.mapper.ResourceFor(resource.WithVersion("")) if err != nil { return "", gvr, fmt.Errorf("unable to get full preferred group-version-resource for %s: %v", resource.String(), err) } // 获取gvr的gv,这一步也是apiPathFor的参数需求 groupVer := gvr.GroupVersion() // 获取rest client的绝对路径 return c.apiPathFor(groupVer), gvr, nil } // namespacedScaleClient 是一个用于在给定命名空间中获取Scale的 ScaleInterface. type namespacedScaleClient struct { // 代理ScaleClient client *scaleClient // 限定该代理ScaleClient的namespace namespace string } // 以namespace为参数,构建一个namespacedScaleClient func (c *scaleClient) Scales(namespace string) ScaleInterface { return &namespacedScaleClient{ client: c, namespace: namespace, } } // 实现ScaleInterface接口的Get方法 func (c *namespacedScaleClient) Get(ctx context.Context, resource schema.GroupResource, name string, opts metav1.GetOptions) (*autoscaling.Scale, error) { // 根据c中的client获取和apserver交互的path和gvr path, gvr, err := c.client.pathAndVersionFor(resource) if err != nil { return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err) } // 调用rest client和apiserver交互,获取resource的子资源scale result := c.client.clientBase.Get(). AbsPath(path). NamespaceIfScoped(c.namespace, c.namespace != ""). Resource(gvr.Resource). Name(name). SubResource("scale"). SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1). Do(ctx) if err := result.Error(); err != nil { return nil, err } // 转化为autoscaling/v1.Scale return convertToScale(&result) } // 实现ScaleInterface接口的Update方法 func (c *namespacedScaleClient) Update(ctx context.Context, resource schema.GroupResource, scale *autoscaling.Scale, opts metav1.UpdateOptions) (*autoscaling.Scale, error) { // 根据c中的client获取和apserver交互的path和gvr path, gvr, err := c.client.pathAndVersionFor(resource) if err != nil { return nil, fmt.Errorf("unable to get client for %s: %v", resource.String(), err) } // 弄清楚我们在这里实际需要gvk(scale) desiredGVK, err := c.client.scaleKindResolver.ScaleForResource(gvr) if err != nil { return nil, fmt.Errorf("could not find proper group-version for scale subresource of %s: %v", gvr.String(), err) } // 将此scale转换为想要的gv scaleUpdate, err := scaleConverter.ConvertToVersion(scale, desiredGVK.GroupVersion()) if err != nil { return nil, fmt.Errorf("could not convert scale update to external Scale: %v", err) } // LegacyCodec形成的codecs(encode编码为符合参数gv的json格式byte[],decode时只解码而不类型转化) encoder := scaleConverter.codecs.LegacyCodec(desiredGVK.GroupVersion()) // 编码scaleUpdate为符合desiredGVK.GroupVersion()的json格式byte[] scaleUpdateBytes, err := runtime.Encode(encoder, scaleUpdate) if err != nil { return nil, fmt.Errorf("could not encode scale update to external Scale: %v", err) } // 调用rest client操作apiserver修改对应gvr的scale子资源 result := c.client.clientBase.Put(). AbsPath(path). NamespaceIfScoped(c.namespace, c.namespace != ""). Resource(gvr.Resource). Name(scale.Name). SubResource("scale"). SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1). Body(scaleUpdateBytes). Do(ctx) if err := result.Error(); err != nil { return nil, err } // 转化为autoscaling/v1.Scale return convertToScale(&result) } // 实现ScaleInterface接口的Patch方法 func (c *namespacedScaleClient) Patch(ctx context.Context, gvr schema.GroupVersionResource, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions) (*autoscaling.Scale, error) { // 获取gvr对应的gv groupVersion := gvr.GroupVersion() // 调用rest client操作apiserver patch对应gvr的scale子资源 result := c.client.clientBase.Patch(pt). // 获取操作对应gv的apiserver path AbsPath(c.client.apiPathFor(groupVersion)). NamespaceIfScoped(c.namespace, c.namespace != ""). Resource(gvr.Resource). Name(name). SubResource("scale"). SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1). Body(data). Do(ctx) if err := result.Error(); err != nil { return nil, err } // 转化为autoscaling/v1.Scale return convertToScale(&result) }
- scheme包
主要是定义了注册到scheme和内部scale的定义