# 用 ChatGPT 写了一点代码

ChatGPT 发布之后，一直在半真半假的用着：有时候写一些代码片段，例如让他用 Python、Rust 分别帮我写一点方法级别的代码；有时候会跟他“探讨”一些问题——是的你没看错，真的是探讨一些问题，例如刚刚我跟他探讨了一下 SRE、DevOps 和平台工程的区别、联系和发展情况。总的说来，如果不求输出成品，那效果还是相当不错的，至少触发灵感是没问题的。

前些天跟蔡书夸下海口，用 ChatGPT 也能给 PP 写个 Ingress 控制器来，奈何俗务缠身，一直也没能完成，今天抽出两个小时，总算完成了一个 Ingress 监听的原型代码。大体过程讲解一下。

我的第一个 Prompt：

~~~plaintext
我想用循序渐进的方式逐步用 Golang 开发一个 Ingress 控制器，我想到的步骤包括：

1. 创建目录布局 
2. 编写第一个可执行的控制台应用的圆形，并完成构建和测试运行 
3. 在这个原型基础上，加入连接 Kubernetes 集群的能力，同样完成构建和运行 
4. 加入 Watch Ingress 对象的能力。并在 Ingress 发生变化的时候输出提示信息。
首先我们完成第一个步骤，使用 shell 命令创建对应的目录结构。

~~~

是的，你没看错，我连目录结构都让他给我创建。ChatGPT 的回复很不错——必须很不错，这个太简单了：

~~~shell
mkdir -p my-ingress-controller/cmd/my-ingress-controller
mkdir -p my-ingress-controller/pkg/ingress
mkdir -p my-ingress-controller/pkg/kube
touch my-ingress-controller/cmd/my-ingress-controller/main.go
touch my-ingress-controller/pkg/ingress/ingress.go
touch my-ingress-controller/pkg/kube/kube.go
~~~

并且给出了解释：

> 这将创建一个名为 `my-ingress-controller` 的目录，并在其中创建 `cmd` 和 `pkg` 目录，分别用于存放主应用程序和包。此外，我们在 `cmd/my-ingress-controller` 目录中创建了 `main.go` 文件，用于编写第一个可执行的控制台应用程序的圆形；在 `pkg/ingress` 和 `pkg/kube` 目录中，分别创建了 `ingress.go` 和 `kube.go` 文件，用于存放后续的代码。

我照章办事执行之后，当然生成了对应的目录和文件，接下来我给出第二个 Prompt：

> 然后完成第二步骤，完成一个可执行的控制台应用的圆形。

ChatGPT 给出了经典的 Hello World 代码，填充了 `my-ingress-controller/cmd/my-ingress-controller/main.go`：

~~~go
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, Ingress!")
}
~~~

接着他解释了代码的作用，提示使用 `go build -o my-ingress-controller ./cmd/my-ingress-controller` 命令进行构建。

对此我的回复是

> `go build 执行失败了，返回信息“go: go.mod file not found in current directory or any parent directory; see 'go help modules'”`

ChatGPT 回应大意是：

> 1. 这是因为 Go 1.11 版本后引入了模块（module）的概念，用于解决依赖管理的问题。如果我们的项目使用了 Go 模块，则需要在项目根目录中创建一个 go.mod 文件来记录项目的依赖关系。
> 2. `go mod init my-ingress-controller`
> 3. 如果你使用的是 Go 1.16 及以上的版本，也可以使用以下命令自动初始化 Go 模块：`go mod init`

照章办事之后，果然构建通过了。

运行成功后，我们开始连接 Kubernetes，对此我给出的 Prompt 是：

> 这一步运行成功之后，我们完成第三个步骤，加入连接 Kubernetes 集群的代码。并再次进行构建和试运行。

ChatGPT 并未含糊，给出了 `kube.go` 和 `main.go` 的代码：

~~~go
// kube.go 两种方法进行连接
package kube

import (
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
    "k8s.io/client-go/rest"
)

func NewClient() (*kubernetes.Clientset, error) {
    config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
    if err != nil {
        return nil, err
    }
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        return nil, err
    }
    return clientset, nil
}

func InClusterConfig() (*rest.Config, error) {
    return rest.InClusterConfig()
}

//main.go：列出 Namespace

package main

import (
    "fmt"
    "my-ingress-controller/pkg/kube"
)

func main() {
    client, err := kube.NewClient()
    if err != nil {
        fmt.Println(err)
        return
    }
    namespaces, err := client.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{})
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, ns := range namespaces.Items {
        fmt.Println(ns.Name)
    }
}
~~~

在这个步骤之后，ChatGPT 开始不太灵了，主要出现的问题包括：

1. Client-Go 和 Kubernetes 之间存在对应关系，他对于这方面并没有一个稳定的假设，导致 API 的兼容频频出错
1. 偶尔会在 Import 方面出现疏漏

Import 问题基本上来说，贴出构建错误就能解决；而版本问题则需要反复调试和提示才得以解决。

最后的一个坑是，他给我生成的 Informer 语句如下：

~~~go
informerFactory := informers.NewSharedInformerFactoryWithOptions(
    client, 
    0,
    informers.WithNamespace("default"),
    informers.WithTweakListOptions(func(options *metav1.ListOptions) {
        options.LabelSelector = "app=my-ingress-controller"
    }),
)
~~~

这里暗含了对命名空间和标签的要求，导致测试无法通过。在我发现问题发出疑问时，这位很淡定的回答：

> 是的，如果要测试 Ingress 资源的监控，可以在创建 Ingress 资源的时候添加一个 `app=my-ingress-controller` 的标签，这样就会触发事件并输出日志信息。

## 也算总结吧

所以以后学编程，是不是可以不用费劲加那么多啥微信群了？
