1
0
Fork 0
mirror of https://github.com/kyverno/kyverno.git synced 2024-12-14 11:57:48 +00:00
kyverno/hack/main.go

323 lines
8.9 KiB
Go
Raw Normal View History

package main
import (
"fmt"
"os"
"path"
"reflect"
"regexp"
"strconv"
"strings"
"text/template"
"github.com/kyverno/kyverno/pkg/client/clientset/versioned"
"k8s.io/client-go/kubernetes"
)
var (
matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
)
func lookupImports(in reflect.Type) map[string]string {
imports := map[string]string{}
for i := 0; i < in.NumMethod(); i++ {
clientType := in.Method(i).Type.Out(0)
imports["client_"+strings.ToLower(clientType.Name())] = clientType.PkgPath()
for j := 0; j < clientType.NumMethod(); j++ {
resourceType := clientType.Method(j).Type.Out(0)
imports["client_"+strings.ToLower(clientType.Name())+"_"+strings.ToLower(resourceType.Name())] = resourceType.PkgPath()
for k := 0; k < resourceType.NumMethod(); k++ {
method := resourceType.Method(k)
prefix := "api_" + strings.ToLower(clientType.Name()) + "_" + strings.ToLower(resourceType.Name()) + "_" + strings.ToLower(method.Name)
for a := 0; a < method.Type.NumIn(); a++ {
arg := method.Type.In(a)
if arg.Kind() == reflect.Pointer {
arg = arg.Elem()
}
p := arg.PkgPath()
if p != "" {
imports[prefix+"_in_"+strconv.Itoa(a)] = p
}
}
for a := 0; a < method.Type.NumOut(); a++ {
arg := method.Type.Out(a)
if arg.Kind() == reflect.Pointer {
arg = arg.Elem()
}
p := arg.PkgPath()
if p != "" {
imports[prefix+"_out_"+strconv.Itoa(a)] = p
}
}
}
}
}
imports2 := map[string]string{}
imports2["context"] = "context"
imports2["metav1"] = "k8s.io/apimachinery/pkg/apis/meta/v1"
imports2["types"] = "k8s.io/apimachinery/pkg/types"
imports2["restclient"] = "k8s.io/client-go/rest"
imports2["watch"] = "k8s.io/apimachinery/pkg/watch"
imports2["metrics"] = "github.com/kyverno/kyverno/pkg/metrics"
imports2["discovery"] = "k8s.io/client-go/discovery"
for _, v := range imports {
if v != "" {
k := strings.ReplaceAll(v, ".", "_")
k = strings.ReplaceAll(k, "-", "_")
k = strings.ReplaceAll(k, "/", "_")
imports2[k] = v
}
}
return imports2
}
func lookupImport(in string, imports map[string]string) string {
for k, v := range imports {
if v == in {
return k
}
}
return ""
}
func resolveType(in reflect.Type, imports map[string]string) string {
switch in.Kind() {
case reflect.Pointer:
return "*" + resolveType(in.Elem(), imports)
case reflect.Array:
return "[]" + resolveType(in.Elem(), imports)
case reflect.Slice:
return "[]" + resolveType(in.Elem(), imports)
case reflect.Map:
return "map[" + resolveType(in.Key(), imports) + "]" + resolveType(in.Elem(), imports)
}
pack := lookupImport(in.PkgPath(), imports)
if pack == "" {
return in.Name()
}
return pack + "." + in.Name()
}
func toSnakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
func generateClient(in reflect.Type, folder string) {
imports := lookupImports(in)
tpl := `
package client
import (
versioned {{ Quote .PkgPath }}
{{- range $alias, $package := Imports }}
{{ $alias }} {{ Quote $package }}
{{- end }}
)
type clientset struct {
inner versioned.Interface
{{- range $cmethod := Methods . }}
{{- $clientType := index (Out $cmethod) 0 }}
{{ ToLower $cmethod.Name }} {{ Type $clientType }}
{{- end }}
}
func (c *clientset) Discovery() discovery.DiscoveryInterface {
return c.inner.Discovery()
}
func Wrap(inner versioned.Interface, m metrics.MetricsConfigManager, t metrics.ClientType) versioned.Interface {
return &clientset{
inner: inner,
{{- range $cmethod := Methods . }}
{{- $clientType := index (Out $cmethod) 0 }}
{{ ToLower $cmethod.Name }}: wrap{{ $clientType.Name }}(inner.{{ $cmethod.Name }}(), m, t),
{{- end }}
}
}
func NewForConfig(c *restclient.Config, m metrics.MetricsConfigManager, t metrics.ClientType) (versioned.Interface, error) {
inner, err := versioned.NewForConfig(c)
if err != nil {
return nil, err
}
return Wrap(inner, m, t), nil
}
{{- range $cmethod := Methods . }}
{{- $clientType := index (Out $cmethod) 0 }}
func (c *clientset) {{ $cmethod.Name }}() {{ Type $clientType }} {
return c.{{ ToLower $cmethod.Name }}
}
{{- end }}
{{- range $cmethod := Methods . }}
{{- $clientType := index (Out $cmethod) 0 }}
type wrapped{{ $clientType.Name }} struct {
inner {{ Type $clientType }}
metrics metrics.MetricsConfigManager
clientType metrics.ClientType
}
func wrap{{ $clientType.Name }}(inner {{ Type $clientType }}, metrics metrics.MetricsConfigManager, t metrics.ClientType) {{ Type $clientType }} {
return &wrapped{{ $clientType.Name }}{inner, metrics, t}
}
{{- range $rmethod := Methods $clientType }}
{{- if ne $rmethod.Name "RESTClient" }}
{{- $resourceType := index (Out $rmethod) 0 }}
type wrapped{{ $clientType.Name }}{{ $resourceType.Name }} struct {
inner {{ Type $resourceType }}
recorder metrics.Recorder
}
func wrap{{ $clientType.Name }}{{ $resourceType.Name }}(inner {{ Type $resourceType }}, recorder metrics.Recorder) {{ Type $resourceType }} {
return &wrapped{{ $clientType.Name }}{{ $resourceType.Name }}{inner, recorder}
}
{{- range $emethod := Methods $resourceType }}
func (c *wrapped{{ $clientType.Name }}{{ $resourceType.Name }}) {{ $emethod.Name }}(
{{- range $i, $argType := In $emethod -}}
{{- if IsVariadic $emethod $i -}}
arg{{ $i }} ...{{ Type $argType.Elem }},
{{- else -}}
arg{{ $i }} {{ Type $argType }},
{{- end -}}
{{- end -}}
) (
{{- range $returnType := Out $emethod -}}
{{ Type $returnType }},
{{- end -}}
) {
defer c.recorder.Record({{ Quote (Snake $emethod.Name) }})
return c.inner.{{ $emethod.Name }}(
{{- range $i, $_ := In $emethod -}}
{{- if IsVariadic $emethod $i -}}
arg{{ $i }}...,
{{- else -}}
arg{{ $i }},
{{- end -}}
{{- end -}}
)
}
{{- end }}
func (c *wrapped{{ $clientType.Name }}) {{ $rmethod.Name }}(
{{- range $i, $argType := In $rmethod -}}
arg{{ $i }} {{ Type $argType }},
{{- end -}}
) (
{{- range $returnType := Out $rmethod -}}
{{ Type $returnType }},
{{- end -}}
) {
{{- $returnType := index (Out $rmethod) 0 }}
{{- if IsNamespaced $rmethod }}
recorder := metrics.NamespacedClientQueryRecorder(c.metrics, arg0, {{ Quote (Kind $returnType.Name) }}, c.clientType)
{{- else }}
recorder := metrics.ClusteredClientQueryRecorder(c.metrics, {{ Quote (Kind $returnType.Name) }}, c.clientType)
{{- end }}
return wrap{{ $clientType.Name }}{{ $resourceType.Name }}(c.inner.{{ $rmethod.Name }}(
{{- range $i, $_ := In $rmethod -}}
arg{{ $i }},
{{- end -}}
), recorder)
}
{{- end }}
{{- end }}
func (c *wrapped{{ $clientType.Name }}) RESTClient() restclient.Interface {
return c.inner.RESTClient()
}
{{- end }}
`
tmpl := template.New("xxx")
tmpl.Funcs(
template.FuncMap{
"Imports": func() map[string]string {
return imports
},
"Import": func(t reflect.Type) string {
pkg := t.PkgPath()
for k, v := range imports {
if v == pkg {
return k
}
}
return ""
},
"Methods": func(t reflect.Type) []reflect.Method {
var methods []reflect.Method
for i := 0; i < t.NumMethod(); i++ {
if t.Method(i).Name != "Discovery" {
methods = append(methods, t.Method(i))
}
}
return methods
},
"PkgPath": func(t reflect.Type) string {
return t.String()
},
"Out": func(in reflect.Method) []reflect.Type {
var out []reflect.Type
for i := 0; i < in.Type.NumOut(); i++ {
out = append(out, in.Type.Out(i))
}
return out
},
"In": func(in reflect.Method) []reflect.Type {
var out []reflect.Type
for i := 0; i < in.Type.NumIn(); i++ {
out = append(out, in.Type.In(i))
}
return out
},
"ToLower": func(in string) string {
return strings.ToLower(in)
},
"Quote": func(in string) string {
return `"` + in + `"`
},
"Type": func(in reflect.Type) string {
return resolveType(in, imports)
},
"IsVariadic": func(in reflect.Method, idx int) bool {
return idx == in.Type.NumIn()-1 && in.Type.IsVariadic()
},
"Kind": func(in string) string {
return strings.ReplaceAll(in, "Interface", "")
},
"IsNamespaced": func(in reflect.Method) bool {
return in.Type.NumIn() != 0
},
"Snake": func(in string) string {
return toSnakeCase(in)
},
},
)
if tmpl, err := tmpl.Parse(tpl); err != nil {
panic(err)
} else {
if err := os.MkdirAll(folder, 0o755); err != nil {
panic(fmt.Sprintf("Failed to create directories for %s", folder))
}
file := "clientset.generated.go"
f, err := os.Create(path.Join(folder, file))
if err != nil {
panic(fmt.Sprintf("Failed to create file %s", path.Join(folder, file)))
}
if err := tmpl.Execute(f, in); err != nil {
panic(err)
}
}
}
func main() {
kube := reflect.TypeOf((*kubernetes.Interface)(nil)).Elem()
kyverno := reflect.TypeOf((*versioned.Interface)(nil)).Elem()
generateClient(kube, "pkg/clients/wrappers/kube")
generateClient(kyverno, "pkg/clients/wrappers/kyverno")
}