解析 YAML 清单到 Kubernetes []client.Object。

huangapple go评论87阅读模式
英文:

Parse YAML manifests to Kubernetes []client.Object

问题

我有一个定义了多个不同类型的Kubernetes资源的YAML文件(根据YAML规范使用---进行分隔):

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # ...
spec:
  # ...
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # ...
rules:
  # ...
---
# 等等

现在,我想将这个列表解析为client.Object实例的切片,以便我可以应用一些过滤和转换,并最终将它们发送到集群中,使用以下代码:

myClient.Patch( // myClient是一个client.Client实例
  ctx,
  object,       // object需要是一个client.Object
  client.Apply,
  client.ForceOwnership,
  client.FieldOwner("my.operator.acme.inc"),
)

然而,我无论如何都无法弄清楚如何从YAML文档中获取到[]client.Object。下面的代码几乎可以实现:

results := make([]client.Object, 0)

scheme := runtime.NewScheme()
clientgoscheme.AddToScheme(scheme)
apiextensionsv1beta1.AddToScheme(scheme)
apiextensionsv1.AddToScheme(scheme)

decode := serializer.NewCodecFactory(scheme).UniversalDeserializer().Decode
data, err := ioutil.ReadAll(reader)
if err != nil {
	return nil, err
}
for _, doc := range strings.Split(string(data), "---") {
	object, gvk, err := decode([]byte(doc), nil, nil)
	if err != nil {
		return nil, err
	}

    // object现在是一个runtime.Object,gvk是一个schema.GroupVersionKind
    // 综合起来,它们拥有我需要暴露一个client.Object的所有信息(我认为),但我不知道如何实际构造一个实现该接口的类型

    result = append(result, ?????)

}

return result, nil

当然,我完全可以接受其他的解析器实现,但我还没有找到任何可以让我进一步的东西。但这似乎在Kubernetes世界中肯定是一个已解决的问题...那么我该如何做呢?

英文:

I have a YAML file defining multiple Kubernetes resources of various types (separated with --- according to the YAML spec):

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  # ...
spec:
  # ...
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # ...
rules:
  # ...
---
# etc

Now, I want to parse this list into a slice of client.Object instances, so I can apply some filtering and transforms, and eventually send them to the cluster using

myClient.Patch( # myClient is a client.Client instance
  ctx,
  object,       # object needs to be a client.Object
  client.Apply,
  client.ForceOwnership,
  client.FieldOwner("my.operator.acme.inc"),
)

However, I can't for the life of me figure out how to get from the YAML doc to []client.Object. The following gets me almost there:

results := make([]client.Object, 0)

scheme := runtime.NewScheme()
clientgoscheme.AddToScheme(scheme)
apiextensionsv1beta1.AddToScheme(scheme)
apiextensionsv1.AddToScheme(scheme)

decode := serializer.NewCodecFactory(scheme).UniversalDeserializer().Decode
data, err := ioutil.ReadAll(reader)
if err != nil {
	return nil, err
}
for _, doc := range strings.Split(string(data), "---") {
	object, gvk, err := decode([]byte(doc), nil, nil)
	if err != nil {
		return nil, err
	}

    // object is now a runtime.Object, and gvk is a schema.GroupVersionKind
    // taken together, they have all the information I need to expose a
    // client.Object (I think) but I have no idea how to actually construct a
    // type that implements that interface

    result = append(result, ?????)

}

return result, nil

I am totally open to other parser implementations, of course, but I haven't found anything that gets me any further. But this seems like it must be a solved problem in the Kubernetes world... so how do I do it?

答案1

得分: 4

我终于成功让它工作了!以下是代码的翻译部分:

import (
	"k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"

	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
)

func deserialize(data []byte) (*client.Object, error) {
	apiextensionsv1.AddToScheme(scheme.Scheme)
	apiextensionsv1beta1.AddToScheme(scheme.Scheme)
	decoder := scheme.Codecs.UniversalDeserializer()

	runtimeObject, groupVersionKind, err := decoder.Decode(data, nil, nil)
	if err != nil {
		return nil, err
	}

	return runtimeObject.(*client.Object), nil
}

有几个关键点(但我不确定我的理解是否100%正确):

  • 虽然decoder.Decode声明的返回类型是(runtime.Object, *scheme.GroupVersionKind, error),但该元组的第一个返回项实际上是client.Object,可以无问题地进行类型转换。
  • 通过在添加apiextensions.k8s.io组之前使用scheme.Scheme作为基线,我可以免费注册所有“标准”资源。
  • 如果我使用scheme.Codecs.UniversalDecoder(),会出现关于“在scheme“pkg/runtime/scheme.go:100”中未为组“apiextensions.k8s.io”的内部版本注册kind“CustomResourceDefinition”的错误,并且返回的groupVersionKind实例的版本显示为__internal。不知道为什么会发生这种情况,或者为什么当我使用UniversalDeserializer()时它不会发生。
英文:

I was finally able to make it work! Here's how:

import (
	"k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"

	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
)

func deserialize(data []byte) (*client.Object, error) {
   	apiextensionsv1.AddToScheme(scheme.Scheme)
	apiextensionsv1beta1.AddToScheme(scheme.Scheme)
	decoder := scheme.Codecs.UniversalDeserializer()

	runtimeObject, groupVersionKind, err := decoder.Decode(data, nil, nil)
	if err != nil {
		return nil, err
	}

	return runtime
}

A couple of things that seem key (but I'm not sure my understanding is 100% correct here):

  • while the declared return type of decoder.Decode is (runtime.Object, *scheme.GroupVersionKind, error), the returned first item of that tuple is actually a client.Object and can be cast as such without problems.
  • By using scheme.Scheme as the baseline before adding the apiextensions.k8s.io groups, I get all the "standard" resources registered for free.
  • If I use scheme.Codecs.UniversalDecoder(), I get errors about no kind "CustomResourceDefinition" is registered for the internal version of group "apiextensions.k8s.io" in scheme "pkg/runtime/scheme.go:100", and the returned groupVersionKind instance shows __internal for version. No idea why this happens, or why it doesn't happen when I use the UniversalDeserializer() instead.

huangapple
  • 本文由 发表于 2021年9月21日 22:02:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/69270267.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定