本文承接上文
每期一个小窍门: 使用Gin 与 client-go 操作k8s (上)
后面应该还会有个下 应该是个operator的全程demo
项目结构如下
client.go
package client
import (
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"superclient/conf"
"sync"
)
type MyClient struct {
ClientSet *kubernetes.Clientset
DiscoveryClient *discovery.DiscoveryClient
}
var (
clientInstance *MyClient = nil
lock = sync.Mutex{}
config *rest.Config
)
func GetMyClient() (mc MyClient) {
var (
err error
)
config, err = clientcmd.BuildConfigFromFlags("", conf.Kubeconfig)
if err != nil {
return
}
mc.ClientSet, err = kubernetes.NewForConfig(config)
if err != nil {
return
}
mc.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(config)
return
}
func (m *MyClient) ClientSet2() *kubernetes.Clientset {
return m.ClientSet
}
func GetHttpConfig() *rest.Config {
return config
}
informer.go
package informer
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/informers"
"superclient/core/pkg/client"
"time"
)
var (
sif informers.SharedInformerFactory
)
func NewSharedInformerFactory(stopCh <-chan struct{}) error {
// 加载定制化客户端
var (
myClient client.MyClient
)
myClient = client.GetMyClient()
// 实例化 sharedInformerFactory
sif = informers.NewSharedInformerFactory(myClient.ClientSet2(), time.Second*10)
// 制作 gvr
gvrs := []schema.GroupVersionResource{
{Group: "", Version: "v1", Resource: "pods"},
{Group: "", Version: "v1", Resource: "services"},
{Group: "", Version: "v1", Resource: "namespaces"},
{Group: "apps", Version: "v1", Resource: "deployments"},
{Group: "apps", Version: "v1", Resource: "statefulsets"},
{Group: "apps", Version: "v1", Resource: "daemonsets"},
}
// 通过gvr 指定启动哪些informer
for _, gvr := range gvrs {
_, err := sif.ForResource(gvr)
if err != nil {
return err
}
}
// 启动所有informer
sif.Start(stopCh)
// 等待informer 全量同步数据完成
sif.WaitForCacheSync(stopCh)
return nil
}
func Setup(stopCh <-chan struct{}) (err error) {
err = NewSharedInformerFactory(stopCh)
if err != nil {
return err
}
return nil
}
func Get() informers.SharedInformerFactory {
return sif
}
三个rest
- 查询pods
- 动态拼接 gvr 自动匹配indexer 并返回查询结果
- 路由转发到 apiserver 内容由apiserver 返回
mian.go
package main
import (
"github.com/gin-gonic/gin"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/proxy"
"k8s.io/client-go/rest"
"net/http"
"superclient/core/pkg/client"
"superclient/core/pkg/informer"
)
func main() {
stopCh := make(chan struct{})
err := informer.Setup(stopCh)
if err != nil {
panic(err.Error())
}
// 启动Gin web服务
// 实例化 Gin
g := gin.Default()
// 写路由
// 直接查询某一种资源数据的
g.GET("/pod/list", func(c *gin.Context) {
items, err := informer.Get().Core().V1().Pods().Lister().List(labels.Everything())
if err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 40000, // 20000 的返回值表示正常,其他表示错误
"message": err.Error(),
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 20000, // 20000 的返回值表示正常,其他表示错误
"message": "success",
"data": items,
})
})
g.GET("/:resource/:group/:version", func(context *gin.Context) {
gvr := schema.GroupVersionResource{
Group: context.Param("group"),
Version: context.Param("version"),
Resource: context.Param("resource"),
}
informer, err := informer.Get().ForResource(gvr)
if err != nil {
return
}
pods, err := informer.Lister().List(labels.Everything())
if err != nil {
return
}
context.JSON(http.StatusOK, gin.H{
"code": 20000,
"data": pods,
})
})
g.Any("/apis/*action", func(context *gin.Context) {
t, err := rest.TransportFor(client.GetHttpConfig())
if err != nil {
panic(err.Error())
}
s := *context.Request.URL
s.Host = "47.98.168.126:6443"
s.Scheme = "https"
httpProxy := proxy.NewUpgradeAwareHandler(&s, t, true, false, nil)
httpProxy.UpgradeTransport = proxy.NewUpgradeRequestRoundTripper(t, t)
httpProxy.ServeHTTP(context.Writer, context.Request)
context.JSON(http.StatusOK, gin.H{
"code": 20000,
"message": "success",
"data": nil,
})
})
_ = g.Run(":8080")
}
不论是请求转发到apiserver 还是动态gvr参数的请求 都是可以的