<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <id>http://lernfunk.de/media/654321</id>
  <title>double12gzh - Posts tagged crd</title>
  <updated>2020-11-08T14:11:59.389240+00:00</updated>
  <link href="double12gzhblogs.readthedocs.io"/>
  <link href="double12gzhblogs.readthedocs.io/blogs/tag/crd/atom.xml"/>
  <generator uri="https://ablog.readthedocs.org" version="0.10.9">ABlog</generator>
  <entry>
    <id>double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-09-26-%E4%BD%BF%E7%94%A8client-go%E8%AE%BF%E9%97%AEk8s%E9%9B%86%E7%BE%A4%E4%B8%AD%E7%9A%84CRD.html</id>
    <title>如何使用client-go访问k8s crd</title>
    <updated>2020-09-26T00:00:00+00:00</updated>
    <author>
      <name>大海星</name>
    </author>
    <content type="html">&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/2020-09-26-k8s-logo.png" /&gt;
&lt;/div&gt;
&lt;div class="section" id="client-gok8s-crd"&gt;

&lt;p&gt;摘要：介绍如何使用clinet-go手动编写程序来访问k8s中的crd。&lt;/p&gt;
&lt;div class="section" id="id1"&gt;
&lt;h2&gt;1. 写在前面&lt;/h2&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;微信公众号：&lt;strong&gt;[double12gzh]&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;个人主页: &lt;a class="reference external" href="https://gzh.readthedocs.io"&gt;https://gzh.readthedocs.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;关注容器技术、关注&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Kubernetes&lt;/span&gt;&lt;/code&gt;。问题或建议，请公众号留言。&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;Kubernetes架构的设计模式，我们可以很方便的使用&lt;a class="reference external" href="https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/"&gt;CRD(Custom Resource
Definitions)&lt;/a&gt;对k8s
API进行扩展。但是问题，通过&lt;a class="reference external" href="https://github.com/kubernetes/client-go"&gt;client-go&lt;/a&gt;来获取这些CRD或开发用户自定义控制器，那是比较麻烦的一件事情，除此之外，市面上对于&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;client-go&lt;/span&gt;&lt;/code&gt;的介绍并不是很多。&lt;/p&gt;
&lt;p&gt;本文将会通过一个示例，简单介绍一下如何通过client-go获取CRD。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id2"&gt;
&lt;h2&gt;2. 写作动机&lt;/h2&gt;
&lt;p&gt;我在PaaS平台的日常开发工作中，想要将第三方存储厂商集成到Kubernetes集群中时，遇到了这个挑战。计划是使用自定义资源定义来定义诸如文件系统池和文件系统。然后，一个自定义的Operator可以监听这些资源的创建和删除，并负责这些资源的生命周期的管理
。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="cr-custom-resource"&gt;
&lt;h2&gt;3. 定义CR(Custom Resource)&lt;/h2&gt;
&lt;p&gt;在本文中，我们将以一个简单的例子来进行演示。使用kubectl可以很容易地创建自定义资源定义，对于这个例子，我们将从一个简单的资源定义开始做起：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="test-yaml"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;test.yaml&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;apiextensions.k8s.io/v1beta1&amp;quot;&lt;/span&gt;
 &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;CustomResourceDefinition&amp;quot;&lt;/span&gt;
 &lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;projects.examples-gzh.com&amp;quot;&lt;/span&gt;
 &lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;examples-gzh.com&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;v1alpha1&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Namespaced&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;names&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;singular&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;project&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;Project&amp;quot;&lt;/span&gt;
     &lt;span class="nt"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
     &lt;span class="nt"&gt;openAPIV3Schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p p-Indicator"&gt;[&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;spec&amp;quot;&lt;/span&gt;&lt;span class="p p-Indicator"&gt;]&lt;/span&gt;
         &lt;span class="nt"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nt"&gt;replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
             &lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;integer&amp;quot;&lt;/span&gt;
             &lt;span class="nt"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;确定&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Group&lt;/span&gt;&lt;/code&gt;的名字。&lt;/p&gt;
&lt;p&gt;在定义CRD时，我们首先需要定义它所在的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Group&lt;/span&gt;&lt;/code&gt;(在上面的代码中，其``Group``为：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;examples-gzh.com&lt;/span&gt;&lt;/code&gt;)。对于Group的定义，为了避免命名冲突，通常会使用一些比较特别的字符串(如：你的个人主页的地址、你公司的域名等)，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Group&lt;/span&gt;&lt;/code&gt;名&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-26-使用client-go访问k8s集群中的CRD.rst&lt;/span&gt;, line 70)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;字确定了之后，由于CRD的名字是按&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;&amp;lt;plural-resource-name&amp;gt;.&amp;lt;api-group-name&amp;gt;&lt;/span&gt;&lt;/code&gt;这个格式进行命名的，所以这里我们的CRD的名字为&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projects.example-gzh.com&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;确定&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;version&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这里的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;version&lt;/span&gt;&lt;/code&gt;，即&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;spec.version&lt;/span&gt;&lt;/code&gt;。如果你的代码没有开发完成，或者还在快速迭代中，那么，建议你使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;alpha&lt;/span&gt;&lt;/code&gt;这样的命名规则。这样的好处是，如果别人想使用你的代码去使用，那么，他单从版本号上就可以很方便的快速知道，你这&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-26-使用client-go访问k8s集群中的CRD.rst&lt;/span&gt;, line 75)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;是一个不稳定的版本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;schema校验&lt;/p&gt;
&lt;p&gt;在上面我们的CRD中，我们引入了&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;spec.validation.openAPIV3Schema&lt;/span&gt;&lt;/code&gt;，它的作用是对其中的字段进行校验，如果用户在使用我们的CRD时，提供了一个不符合要求的字段后，validation可以很方便的对其进行校验。除了在这里引用validation之外，我们还&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="system-message"&gt;
&lt;p class="system-message-title"&gt;System Message: WARNING/2 (&lt;span class="docutils literal"&gt;/home/docs/checkouts/readthedocs.org/user_builds/gzh/checkouts/latest/blogs/Kubernetes/2020-09-26-使用client-go访问k8s集群中的CRD.rst&lt;/span&gt;, line 80)&lt;/p&gt;
&lt;p&gt;Bullet list ends without a blank line; unexpected unindent.&lt;/p&gt;
&lt;/div&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;可以选择在admintion&lt;/dt&gt;&lt;dd&gt;&lt;p&gt;controller中通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Validate&lt;/span&gt;&lt;/code&gt;阶段进行验证，不过样是需要开启admission
webhook的。&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;将上面的代码保存到一个文件中之后，我们就可以通过&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;apply&lt;/span&gt; &lt;span class="pre"&gt;-f&lt;/span&gt; &lt;span class="pre"&gt;demo.yaml&lt;/span&gt;&lt;/code&gt;进行部署了。我在本机通过minikue启动了一个K8S集群：&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&amp;gt; kubectl get po -A
NAMESPACE              NAME                                        READY   STATUS    RESTARTS   AGE
kube-system            coredns-f9fd979d6-7h2b7                     &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kube-system            etcd-minikube                               &lt;span class="m"&gt;0&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            kube-apiserver-minikube                     &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            kube-controller-manager-minikube            &lt;span class="m"&gt;0&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            kube-proxy-p8zb7                            &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kube-system            kube-scheduler-minikube                     &lt;span class="m"&gt;0&lt;/span&gt;/1     Running   &lt;span class="m"&gt;2&lt;/span&gt;          9h
kube-system            storage-provisioner                         &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kubernetes-dashboard   dashboard-metrics-scraper-c95fcf479-gvhpd   &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h
kubernetes-dashboard   kubernetes-dashboard-5c448bc4bf-lpwqh       &lt;span class="m"&gt;1&lt;/span&gt;/1     Running   &lt;span class="m"&gt;1&lt;/span&gt;          9h

PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&amp;gt; kubectl version
Client Version: version.Info&lt;span class="o"&gt;{&lt;/span&gt;Major:&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;, Minor:&lt;span class="s2"&gt;&amp;quot;16+&amp;quot;&lt;/span&gt;, GitVersion:&lt;span class="s2"&gt;&amp;quot;v1.16.6-beta.0&amp;quot;&lt;/span&gt;, GitCommit:&lt;span class="s2"&gt;&amp;quot;e7f962ba86f4ce7033828210ca3556393c377bcc&amp;quot;&lt;/span&gt;, GitTreeState:&lt;span class="s2"&gt;&amp;quot;clean&amp;quot;&lt;/span&gt;, BuildDate:&lt;span class="s2"&gt;&amp;quot;2020-01-15T08:26:26Z&amp;quot;&lt;/span&gt;, GoVersion:&lt;span class="s2"&gt;&amp;quot;go1.13.5&amp;quot;&lt;/span&gt;, Compiler:&lt;span class="s2"&gt;&amp;quot;gc&amp;quot;&lt;/span&gt;, Platform:&lt;span class="s2"&gt;&amp;quot;windows/amd64&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
Server Version: version.Info&lt;span class="o"&gt;{&lt;/span&gt;Major:&lt;span class="s2"&gt;&amp;quot;1&amp;quot;&lt;/span&gt;, Minor:&lt;span class="s2"&gt;&amp;quot;19&amp;quot;&lt;/span&gt;, GitVersion:&lt;span class="s2"&gt;&amp;quot;v1.19.2&amp;quot;&lt;/span&gt;, GitCommit:&lt;span class="s2"&gt;&amp;quot;f5743093fd1c663cb0cbc89748f730662345d44d&amp;quot;&lt;/span&gt;, GitTreeState:&lt;span class="s2"&gt;&amp;quot;clean&amp;quot;&lt;/span&gt;, BuildDate:&lt;span class="s2"&gt;&amp;quot;2020-09-16T13:32:58Z&amp;quot;&lt;/span&gt;, GoVersion:&lt;span class="s2"&gt;&amp;quot;go1.15&amp;quot;&lt;/span&gt;, Compiler:&lt;span class="s2"&gt;&amp;quot;gc&amp;quot;&lt;/span&gt;, Platform:&lt;span class="s2"&gt;&amp;quot;linux/amd64&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;部署我们的CRD:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl apply -f .&lt;span class="se"&gt;\U&lt;/span&gt;ntitled-2.yaml
customresourcedefinition.apiextensions.k8s.io/projects.examples-gzh.com created

PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl get crd
NAME                        CREATED AT
projects.examples-gzh.com   &lt;span class="m"&gt;2020&lt;/span&gt;-09-25T10:40:01Z
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;如果需要查看其详情，可以使用命令:
&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;kubectl&lt;/span&gt; &lt;span class="pre"&gt;describe&lt;/span&gt; &lt;span class="pre"&gt;crd&lt;/span&gt; &lt;span class="pre"&gt;projects.examples-gzh.com&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;既然CRD已经创建完成了，接下来我们看一下如何使用这个CRD来创建与之相对应的CR。CR相关的文件内容如下：&lt;/p&gt;
&lt;div class="highlight-yaml notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nt"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;examples-gzh.com/v1alpha1&amp;quot;&lt;/span&gt;
&lt;span class="nt"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;Project&lt;/span&gt;
&lt;span class="nt"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;gzh-cr&lt;/span&gt;
  &lt;span class="nt"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;default&lt;/span&gt;
&lt;span class="nt"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="nt"&gt;replica&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="l l-Scalar l-Scalar-Plain"&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;创建CR&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl apply -f cr.yaml
project.examples-gzh.com/gzh-cr created

PS C:&lt;span class="se"&gt;\U&lt;/span&gt;sers&lt;span class="se"&gt;\g&lt;/span&gt;uanzenghui&lt;span class="se"&gt;\D&lt;/span&gt;ocuments&amp;gt; kubectl get Project
NAME     AGE
gzh-cr   39s
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;接下来，我们将使用client-go来获取这个CR。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="golang-client"&gt;
&lt;h2&gt;4. 创建golang client&lt;/h2&gt;
&lt;p&gt;在进行本节前，我假设您已经对client-go、k8s控制器机制有所理解，并且有一定的GoLang的开发经验。&lt;/p&gt;
&lt;p&gt;另外，与其它一些讲解Operator的文章不同的是，这些使用CRD的文档会假设你正在使用某种代码生成器来自动生成客户端库。然而，对于这个过程的文档很少，而且从阅读Github上的一些激烈的讨论中，我们可以看出，它仍然是一个正在进行中的工作。&lt;/p&gt;
&lt;p&gt;本文中，我将坚持使用（大部分）手动实现的客户端的方式给大家展示。&lt;/p&gt;
&lt;p&gt;首先，您可以创建一个自己的项目路径，并安装依赖:&lt;/p&gt;
&lt;div class="highlight-bash notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;mkdir github.com/double12gzh/k8s-crd-demo
go get k8s.io/client-go@v0.17.0
go get k8s.io/apimachinery@v0.17.0
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id3"&gt;
&lt;h3&gt;4.1 定义类型&lt;/h3&gt;
&lt;div class="literal-block-wrapper docutils container" id="id6"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;example.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;

 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectSpec&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;Replicas&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;replicas&amp;quot;`&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt;   &lt;span class="s"&gt;`json:&amp;quot;,inline&amp;quot;`&lt;/span&gt;
   &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMeta&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;metadata,omitempty&amp;quot;`&lt;/span&gt;
   &lt;span class="nx"&gt;Spec&lt;/span&gt; &lt;span class="nx"&gt;ProjectSpec&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;spec&amp;quot;`&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectList&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;,inline&amp;quot;`&lt;/span&gt;
     &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListMeta&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;metadata,omitempty&amp;quot;`&lt;/span&gt;
     &lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="s"&gt;`json:&amp;quot;items&amp;quot;`&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metav1.ObjectMeta&lt;/span&gt;&lt;/code&gt;中包含了一个比较重要的类型&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;metadata&lt;/span&gt;&lt;/code&gt;，k8s中所有的资源有都这个属性，这里面可以定义诸如：&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;name&lt;/span&gt;&lt;/code&gt;，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;namespace&lt;/span&gt;&lt;/code&gt;，&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;label&lt;/span&gt;&lt;/code&gt;等的属性。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="deepcopy"&gt;
&lt;h3&gt;4.2 定义DeepCopy方法&lt;/h3&gt;
&lt;p&gt;Kubernetes API 所服务的每个类型（在本例中，Project 和
ProjectList）都需要实现 k8s.io/apimachinery/pkg/runtime.Object
接口。这个接口定义了两个方法GetObjectKind()和DeepCopyObject()。第一个方法已经由内嵌的metav1.TypeMeta结构提供了；第二个方法你必须自己实现。&lt;/p&gt;
&lt;p&gt;DeepCopyObject方法的目的是生成一个对象的深度拷贝。由于这涉及到大量的模板代码，所以这些方法通常是自动生成的。在本文中，我们将手动进行。继续在同一个包中添加第二个文件
deepcopy.go。&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id7"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;deepcopy.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/runtime&amp;quot;&lt;/span&gt;

 &lt;span class="c1"&gt;// DeepCopyInto 把一个对象的所有属性复制给此对象类型的指针&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;DeepCopyInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ObjectMeta&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Spec&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ProjectSpec&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;Replicas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Spec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Replicas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// DeepCopyObject 返回一个对象类型&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;DeepCopyObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
     &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DeepCopyInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// DeepCopyObject 返回一个对像类型&lt;/span&gt;
 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;DeepCopyObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TypeMeta&lt;/span&gt;
     &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListMeta&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListMeta&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
         &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
             &lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;DeepCopyInto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;out&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;上面这个DeepCopy是我们手动来生成的，你可能已经注意到，定义所有这些不同的
DeepCopy
方法并不是一件很有趣的事情。有很多不同的工具和框架可以自动生成这些方法（所有的文档和整体成熟度都有很大的不同）。我发现效果最好的是控制器生成工具，它是&lt;a class="reference external" href="https://github.com/kubernetes-sigs/kubebuilder"&gt;Kubebuilder&lt;/a&gt;框架的一部分。&lt;/p&gt;
&lt;p&gt;下面我们就来看一下：&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go&lt;/span&gt; &lt;span class="pre"&gt;get&lt;/span&gt; &lt;span class="pre"&gt;-u&lt;/span&gt; &lt;span class="pre"&gt;github.com/kubernetes-sigs/controller-tools/cmd/controller-gen&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;为了能够使用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;controller-gen&lt;/span&gt;&lt;/code&gt;，我们需要在CRD类型上面的添加一个annotation，如下：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id8"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;test.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3
4
5
6
7
8
9&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="c1"&gt;// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object&lt;/span&gt;
 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Project&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="c1"&gt;// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object&lt;/span&gt;
 &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectList&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c1"&gt;// ...&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="admonition tip"&gt;
&lt;p class="admonition-title"&gt;Tip&lt;/p&gt;
&lt;p&gt;说明：对于这些annotation我们没有必要去全部记住，只有当使用到的时候再去查阅一下就行，根据二八原则，只需要记住一些常用的就可以了，其它那些不常用的只需要了解一下。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;写好了上述代码，我们运行一下命令&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;controller-gen&lt;/span&gt; &lt;span class="pre"&gt;object&lt;/span&gt; &lt;span class="pre"&gt;paths=./api/types/v1alpha1/project.go&lt;/span&gt;&lt;/code&gt;即可生成需要代码。&lt;/p&gt;
&lt;p&gt;为了更加的简化，你甚至可以在代码文件的前面加一个声明&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go:generate&lt;/span&gt;&lt;/code&gt;，具体请&lt;a class="reference external" href="https://blog.golang.org/generate"&gt;参考&lt;/a&gt;。如：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id9"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;test.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;1
2
3
4
5
6
7&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;

 &lt;span class="c1"&gt;//go:generate controller-gen object paths=$GOFILE&lt;/span&gt;

 &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;然后只需要在代码的根路径中执行&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;go&lt;/span&gt; &lt;span class="pre"&gt;generate&lt;/span&gt; &lt;span class="pre"&gt;./...&lt;/span&gt;&lt;/code&gt;即可。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="id4"&gt;
&lt;h3&gt;4.3 注册类型&lt;/h3&gt;
&lt;p&gt;接下来，你需要让客户端库知道你的新类型。这将允许客户端在与API服务器通信时（或多或少）自动处理你的新类型。&lt;/p&gt;
&lt;p&gt;为此，在你的包中添加一个新文件 register.go。&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id10"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;register.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;metav1&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/apis/meta/v1&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/runtime&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;k8s.io/apimachinery/pkg/runtime/schema&amp;quot;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GroupName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;example-gzh.com&amp;quot;&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;v1alpha1&amp;quot;&lt;/span&gt;

 &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;SchemeBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewSchemeBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addKnownTypes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;AddToScheme&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;SchemeBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddToScheme&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;addKnownTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddKnownTypes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
         &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="nx"&gt;metav1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddToGroupVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SchemeGroupVersion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;正如你所注意到的，这段代码还没有真正做任何事情（除了创建一个新的runtime.SchemeBuilder实例）。重要的部分是AddToScheme函数（第16行），它是第15行创建的runtime.SchemeBuilder类型的导出结构成员。只要Kubernetes客户端被初始化以注册你的类型定
义，你就可以在以后从客户端代码的任何部分调用这个函数。&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="http-client"&gt;
&lt;h3&gt;4.4 创建HTTP Client&lt;/h3&gt;
&lt;p&gt;在定义了类型并添加了一个方法来在全局方案构建器上注册它们之后，你现在可以创建一个能够加载你的自定义资源的HTTP客户端。&lt;/p&gt;
&lt;p&gt;为此，将以下代码添加到你的包的main.go文件中：&lt;/p&gt;
&lt;div class="literal-block-wrapper docutils container" id="id11"&gt;
&lt;div class="code-block-caption"&gt;&lt;span class="caption-text"&gt;main.go&lt;/span&gt;&lt;/div&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt; 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt; &lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

 &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;flag&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;log&amp;quot;&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime/schema&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime/serializer&amp;quot;&lt;/span&gt;

     &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/client-go/rest&amp;quot;&lt;/span&gt;
     &lt;span class="s"&gt;&amp;quot;ks.io/client-go/tools/clientcmd&amp;quot;&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StringVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;kubeconfig&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;path to Kubernetes config file&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;
     &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;using in-cluster configuration&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InClusterConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;using configuration from &amp;#39;%s&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
         &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;clientcmd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BuildConfigFromFlags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;kubeconfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AddToScheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

     &lt;span class="nx"&gt;crdConfig&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContentConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/apis&amp;quot;&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NegotiatedSerializer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;serializer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewCodecFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Scheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UserAgent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DefaultKubernetesUserAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

     &lt;span class="nx"&gt;exampleRestClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UnversionedRESTClientFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;crdConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;现在你可以使用第48行创建的exampleRestClient来查询example.martin-helmich.de/v1alpha1
API组中的所有自定义资源。例如：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;v1alpha1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;exampleRestClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;为了以一种更安全的方式使用你的API，通常情况下，我们最好在自己的clientet中封装这些操作。为此，创建一个新的子包clientet/v1alpha1。&lt;/p&gt;
&lt;p&gt;首先，实现一个定义你的API组类型的接口，并将配置设置从你的主方法移到该clientet的构造函数中（下面例子中的NewForConfig）。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime/schema&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/rest&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ExampleVAlphaInterface&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ProjectInterface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;restClient&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;NewForConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ContentConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GroupVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;APIPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;/apis&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NegotiatedSerializer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Codecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WithoutConversion&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UserAgent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DefaultKubernetesUserAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RESTClientFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;ProjectInterface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;下面的代码还不能编译，因为它仍然缺少&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ProjectInterface&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projectClient&lt;/span&gt;&lt;/code&gt;类型。我们稍后将讨论这些类型。&lt;/p&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ExampleV1Alpha1Interface&lt;/span&gt;&lt;/code&gt;和它的实现&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;--ExampleV1Alpha1Client&lt;/span&gt;&lt;/code&gt;结构现在是访问自定义资源的中心点。现在，你可以在&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;main.go&lt;/span&gt;&lt;/code&gt;中简单地调用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clientet,&lt;/span&gt; &lt;span class="pre"&gt;err&lt;/span&gt; &lt;span class="pre"&gt;:=&lt;/span&gt; &lt;span class="pre"&gt;v1alpha1.NewForConfig(config)&lt;/span&gt;&lt;/code&gt;来创建一个新的客户集。&lt;/p&gt;
&lt;p&gt;接下来，你需要实现一个特定的&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;clientset&lt;/span&gt;&lt;/code&gt;来访问Project自定义资源（注意，上面的例子已经使用了&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;ProjectInterface&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projectClient&lt;/span&gt;&lt;/code&gt;类型，我们仍然需要提供）。在同一个包中创建第二个文件&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;projects.go&lt;/span&gt;&lt;/code&gt;。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;metav&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/apis/meta/v&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/watch&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/kubernetes/scheme&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/rest&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProjectInterface&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;projectClient&lt;/span&gt; &lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;restClient&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;
    &lt;span class="nx"&gt;ns&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProjectList&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;projectClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Watch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;restClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
        &lt;span class="nx"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;
        &lt;span class="nx"&gt;Namespace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ns&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;VersionedParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ParameterCodec&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
        &lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;这个client显然还不完善，还缺失了删除、更新等方法。不过，这些方法可以和已有的方法类似实现。看看现有的clientset（例如，Pod
clientset）以获得灵感。&lt;/p&gt;
&lt;p&gt;在创建了clientset之后，用它来列出你现有的资源就变得非常容易了。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;fmt&amp;quot;&lt;/span&gt;

    &lt;span class="nx"&gt;clientValpha&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/clientset/valpha&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientValpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewForConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;projects found: %+v\n&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="informer"&gt;
&lt;h3&gt;4.5 生成Informer&lt;/h3&gt;
&lt;p&gt;在构建Kubernetes
Operator时，您通常希望能够对新创建或更新的资源做出反应。理论上，您可以定期调用List()方法，检查是否有新资源被添加。在实践中，这是一个次优的解决方案，尤其是当您有很多这样的资源时。&lt;/p&gt;
&lt;p&gt;大多数Operator的工作方式是通过使用初始List()调用来初始加载资源的所有相关实例，然后使用Watch()调用来订阅更新。然后，初始对象列表和从Watch接收到的更新被用来构建一个本地缓存，允许快速访问任何自定义资源，而不必每次都打到API服务器。&lt;/p&gt;
&lt;p&gt;这种模式非常常见，以至于client-go库为此提供了一个助手：k8s.io/client-go/tools/cache包中的Informer。您可以为您的自定义资源构建一个新的
Informer，如下所示：&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;time&amp;quot;&lt;/span&gt;

    &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/api/types/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;client_valpha&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;github.com/double12gzh/k8s-demo/clientset/valpha&amp;quot;&lt;/span&gt;
    &lt;span class="nx"&gt;metav&lt;/span&gt; &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/apis/meta/v&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/runtime&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/util/wait&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/apimachinery/pkg/watch&amp;quot;&lt;/span&gt;
    &lt;span class="s"&gt;&amp;quot;ks.io/client-go/tools/cache&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nx"&gt;WatchResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientSet&lt;/span&gt; &lt;span class="nx"&gt;client_valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ExampleVAlphaInterface&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;projectStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;projectController&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NewInformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListWatch&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;ListFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;some-namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="nx"&gt;WatchFunc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt; &lt;span class="nx"&gt;metav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ListOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Interface&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Projects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;some-namespace&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;Watch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;valpha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
        &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Minute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ResourceEventHandlerFuncs&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="nx"&gt;projectController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NeverStop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;projectStore&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;NewInformer&lt;/span&gt;&lt;/code&gt;方法返回两个对象。第二个返回值，控制器控制&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;List()&lt;/span&gt;&lt;/code&gt;和&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;Watch()&lt;/span&gt;&lt;/code&gt;调用，并在第一个返回值，即存储中填充一个（或多或少）最近在API服务器上被监视的资源状态的缓存（在本例中，项目CRD）。&lt;/p&gt;
&lt;p&gt;现在，你可以使用 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;store&lt;/span&gt;&lt;/code&gt; 来轻松访问你的 &lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;CRD&lt;/span&gt;&lt;/code&gt;，要么列出所有的
CRD，要么通过名称来访问它们。请记住，存储函数返回的是通用&lt;code class="docutils literal notranslate"&gt;&lt;span class="pre"&gt;interface{}&lt;/span&gt;&lt;/code&gt;类型，所以您必须将它们类型化回您的CRD类型。&lt;/p&gt;
&lt;div class="highlight-go notranslate"&gt;&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;WatchResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;project&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GetByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;some-namespace/some-project&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nx"&gt;v1alpha1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="id5"&gt;
&lt;h2&gt;5. 总结&lt;/h2&gt;
&lt;p&gt;为Custom Resources构建客户端是（至少，目前）只有很少的文档，有时可能会有点棘手。&lt;/p&gt;
&lt;p&gt;如本文所示，为你的Custom
Resource建立一个客户端库，以及相应的Informer是一个很好的起点，可以构建你自己的Kubernetes
Operator，对Custom Resource的变化做出反应。&lt;/p&gt;
&lt;blockquote&gt;
&lt;div&gt;&lt;p&gt;您可以到我的&lt;a class="reference external" href="https://github.com/double12gzh/k8s-demo.git"&gt;github&lt;/a&gt;上查看完整代码&lt;/p&gt;
&lt;/div&gt;&lt;/blockquote&gt;
&lt;hr class="docutils" /&gt;
&lt;p&gt;欢迎关注我的微信公众号：&lt;/p&gt;
&lt;div class="figure align-default"&gt;
&lt;img alt="" src="https://gitee.com/double12gzh/wiki-pictures/raw/master/wechat_public.jpg" /&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</content>
    <link href="double12gzhblogs.readthedocs.io/blogs/Kubernetes/2020-09-26-%E4%BD%BF%E7%94%A8client-go%E8%AE%BF%E9%97%AEk8s%E9%9B%86%E7%BE%A4%E4%B8%AD%E7%9A%84CRD.html" rel="alternate"/>
    <published>2020-09-26T00:00:00+00:00</published>
  </entry>
</feed>
