client-go wrapTransport
发送请求前,支持修改请求内容或者拦截。
发送请求后,支持修改响应内容或者拦截。
结构体实例实现
package main
import (
"context"
"fmt"
"net/http"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
type customRoundTripper struct {
rt http.RoundTripper
}
// customRoundTripper 实现了 http.RoundTripper 接口
func (crt *customRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// 在请求发送前增加自定义逻辑
req.Header.Add("a", "b")
fmt.Println("add header a/b")
resp, err := crt.rt.RoundTrip(req)
if err != nil {
return nil, err
}
// 在请求响应后打印
fmt.Println("response success")
return resp, nil
}
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
fmt.Println(err)
return
}
// 传输层走customRoundTripper
config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
return &customRoundTripper{rt: rt}
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
fmt.Println(err)
return
}
_, err = clientset.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("list namespaces success")
}
匿名函数实例实现
package main
import (
"context"
"fmt"
"net/http"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
type RoundTripAnonymousFunctionImpl func(req *http.Request) (*http.Response, error)
// RoundTripAnonymousFunctionImpl 实现了 http.RoundTripper 接口
func (r RoundTripAnonymousFunctionImpl) RoundTrip(req *http.Request) (*http.Response, error) {
return r(req)
}
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
fmt.Println(err)
return
}
// 传输层走RoundTripAnonymousFunctionImpl
config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
return RoundTripAnonymousFunctionImpl(func(req *http.Request) (*http.Response, error) {
// 在请求发送前增加自定义逻辑
req.Header.Add("a", "b")
fmt.Println("add header a/b")
resp, err := rt.RoundTrip(req)
if err != nil {
return nil, err
}
// 在请求响应后打印
fmt.Println("response success")
return resp, nil
})
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
fmt.Println(err)
return
}
_, err = clientset.CoreV1().Namespaces().List(context.Background(), v1.ListOptions{})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("list namespaces success")
}
kube-apiserver请求带上requestID
package main
import (
"context"
"fmt"
"net/http"
"github.com/google/uuid"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
)
const headerRequestIDKey = "request_id"
type ctxRequestIDKey struct{}
type customRoundTripper struct {
rt http.RoundTripper
}
// customRoundTripper 实现了 http.RoundTripper 接口
func (crt *customRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// 设置ctxRequestIDKey
requestID, _ := req.Context().Value(ctxRequestIDKey{}).(string)
// 发出请求前打印requestID
fmt.Printf("req requestID: %s\n", requestID)
req.Header[headerRequestIDKey] = []string{requestID}
resp, err := crt.rt.RoundTrip(req)
if err != nil {
return nil, err
}
return resp, nil
}
func main() {
config, err := clientcmd.BuildConfigFromFlags("", "/root/.kube/config")
if err != nil {
fmt.Println(err)
return
}
// 传输层走customRoundTripper
config.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
return &customRoundTripper{rt: rt}
}
clientset, err := kubernetes.NewForConfig(config)
if err != nil {
fmt.Println(err)
return
}
uuidStr := uuid.NewString()
ctx := context.WithValue(context.Background(), ctxRequestIDKey{}, uuidStr)
_, err = clientset.CoreV1().Namespaces().List(ctx, v1.ListOptions{})
if err != nil {
fmt.Println(err)
return
}
fmt.Println("list namespaces success")
}
在go http NewRequestWithContext方法中,用户传入的context key/value值保存到request的context。虽然response包含了request,但是response中是全新的request,没有requestID。