1
0
mirror of https://github.com/rancher/norman.git synced 2025-09-03 08:14:40 +00:00

Generate clients in clientset style

This commit is contained in:
Darren Shepherd
2017-11-13 12:50:25 -07:00
parent 591b663b2d
commit 9cb51e9a46
11 changed files with 163 additions and 48 deletions

View File

@@ -25,24 +25,14 @@ type ObjectClient struct {
Factory ObjectFactory Factory ObjectFactory
} }
func NewObjectClient(namespace string, config rest.Config, apiResource *metav1.APIResource, gvk schema.GroupVersionKind, factory ObjectFactory) (*ObjectClient, error) { func NewObjectClient(namespace string, restClient rest.Interface, apiResource *metav1.APIResource, gvk schema.GroupVersionKind, factory ObjectFactory) *ObjectClient {
if config.NegotiatedSerializer == nil {
configConfig := dynamic.ContentConfig()
config.NegotiatedSerializer = configConfig.NegotiatedSerializer
}
restClient, err := rest.UnversionedRESTClientFor(&config)
if err != nil {
return nil, err
}
return &ObjectClient{ return &ObjectClient{
restClient: restClient, restClient: restClient,
resource: apiResource, resource: apiResource,
gvk: gvk, gvk: gvk,
ns: namespace, ns: namespace,
Factory: factory, Factory: factory,
}, nil }
} }
func (p *ObjectClient) Create(o runtime.Object) (runtime.Object, error) { func (p *ObjectClient) Create(o runtime.Object) (runtime.Object, error) {

View File

@@ -37,7 +37,7 @@ type genericController struct {
running bool running bool
} }
func NewGenericController(name string, objectClient *clientbase.ObjectClient) (GenericController, error) { func NewGenericController(name string, objectClient *clientbase.ObjectClient) GenericController {
informer := cache.NewSharedIndexInformer( informer := cache.NewSharedIndexInformer(
&cache.ListWatch{ &cache.ListWatch{
ListFunc: objectClient.List, ListFunc: objectClient.List,
@@ -50,7 +50,7 @@ func NewGenericController(name string, objectClient *clientbase.ObjectClient) (G
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(),
name), name),
name: name, name: name,
}, nil }
} }
func (g *genericController) Informer() cache.SharedIndexInformer { func (g *genericController) Informer() cache.SharedIndexInformer {

View File

@@ -54,7 +54,7 @@ type {{.schema.CodeName}}Interface interface {
List(opts metav1.ListOptions) (*{{.schema.CodeName}}List, error) List(opts metav1.ListOptions) (*{{.schema.CodeName}}List, error)
Watch(opts metav1.ListOptions) (watch.Interface, error) Watch(opts metav1.ListOptions) (watch.Interface, error)
DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error
Controller() ({{.schema.CodeName}}Controller, error) Controller() {{.schema.CodeName}}Controller
} }
type {{.schema.ID}}Controller struct { type {{.schema.ID}}Controller struct {
@@ -85,35 +85,30 @@ func (c {{.schema.ID}}Factory) List() runtime.Object {
return &{{.schema.CodeName}}List{} return &{{.schema.CodeName}}List{}
} }
func New{{.schema.CodeName}}Client(namespace string, config rest.Config) ({{.schema.CodeName}}Interface, error) { func (s *{{.schema.ID}}Client) Controller() {{.schema.CodeName}}Controller {
objectClient, err := clientbase.NewObjectClient(namespace, config, &{{.schema.CodeName}}Resource, {{.schema.CodeName}}GroupVersionKind, {{.schema.ID}}Factory{}) s.client.Lock()
return &{{.schema.ID}}Client{ defer s.client.Unlock()
objectClient: objectClient,
}, err
}
func (s *{{.schema.ID}}Client) Controller() ({{.schema.CodeName}}Controller, error) { c, ok := s.client.{{.schema.ID}}Controllers[s.ns]
s.Lock() if ok {
defer s.Unlock() return c
if s.controller != nil {
return s.controller, nil
} }
controller, err := controller.NewGenericController({{.schema.CodeName}}GroupVersionKind.Kind+"Controller", genericController := controller.NewGenericController({{.schema.CodeName}}GroupVersionKind.Kind+"Controller",
s.objectClient) s.objectClient)
if err != nil {
return nil, err c = &{{.schema.ID}}Controller{
GenericController: genericController,
} }
s.controller = &{{.schema.ID}}Controller{ s.client.{{.schema.ID}}Controllers[s.ns] = c
GenericController: controller,
} return c
return s.controller, nil
} }
type {{.schema.ID}}Client struct { type {{.schema.ID}}Client struct {
sync.Mutex client *Client
ns string
objectClient *clientbase.ObjectClient objectClient *clientbase.ObjectClient
controller {{.schema.CodeName}}Controller controller {{.schema.CodeName}}Controller
} }

View File

@@ -149,6 +149,27 @@ func generateController(outputDir string, schema *types.Schema, schemas *types.S
}) })
} }
func generateK8sClient(outputDir string, version *types.APIVersion, schemas []*types.Schema) error {
filePath := strings.ToLower("zz_generated_k8s_client.go")
output, err := os.Create(path.Join(outputDir, filePath))
if err != nil {
return err
}
defer output.Close()
typeTemplate, err := template.New("k8sClient.template").
Funcs(funcs()).
Parse(strings.Replace(k8sClientTemplate, "%BACK%", "`", -1))
if err != nil {
return err
}
return typeTemplate.Execute(output, map[string]interface{}{
"version": version,
"schemas": schemas,
})
}
func generateClient(outputDir string, schemas []*types.Schema) error { func generateClient(outputDir string, schemas []*types.Schema) error {
template, err := template.New("client.template"). template, err := template.New("client.template").
Funcs(funcs()). Funcs(funcs()).
@@ -177,7 +198,7 @@ func Generate(schemas *types.Schemas, cattleOutputPackage, k8sOutputPackage stri
return err return err
} }
doDeepCopy := false controllers := []*types.Schema{}
generated := []*types.Schema{} generated := []*types.Schema{}
for _, schema := range schemas.Schemas() { for _, schema := range schemas.Schemas() {
@@ -192,7 +213,7 @@ func Generate(schemas *types.Schemas, cattleOutputPackage, k8sOutputPackage stri
if contains(schema.CollectionMethods, http.MethodGet) && if contains(schema.CollectionMethods, http.MethodGet) &&
!strings.HasPrefix(schema.PkgName, "k8s.io") && !strings.HasPrefix(schema.PkgName, "k8s.io") &&
!strings.Contains(schema.PkgName, "/vendor/") { !strings.Contains(schema.PkgName, "/vendor/") {
doDeepCopy = true controllers = append(controllers, schema)
if err := generateController(k8sDir, schema, schemas); err != nil { if err := generateController(k8sDir, schema, schemas); err != nil {
return err return err
} }
@@ -205,10 +226,12 @@ func Generate(schemas *types.Schemas, cattleOutputPackage, k8sOutputPackage stri
return err return err
} }
if doDeepCopy { if len(controllers) > 0 {
if err := deepCopyGen(baseDir, k8sOutputPackage); err != nil { if err := deepCopyGen(baseDir, k8sOutputPackage); err != nil {
return err return err
} }
generateK8sClient(k8sDir, &controllers[0].Version, controllers)
} }
if err := gofmt(baseDir, k8sOutputPackage); err != nil { if err := gofmt(baseDir, k8sOutputPackage); err != nil {

View File

@@ -0,0 +1,62 @@
package generator
var k8sClientTemplate = `package {{.version.Version}}
import (
"sync"
"github.com/rancher/norman/clientbase"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
)
type Interface interface {
RESTClient() rest.Interface
{{range .schemas}}
{{.CodeNamePlural}}Getter{{end}}
}
type Client struct {
sync.Mutex
restClient rest.Interface
{{range .schemas}}
{{.ID}}Controllers map[string]{{.CodeName}}Controller{{end}}
}
func NewForConfig(config rest.Config) (Interface, error) {
if config.NegotiatedSerializer == nil {
configConfig := dynamic.ContentConfig()
config.NegotiatedSerializer = configConfig.NegotiatedSerializer
}
restClient, err := rest.UnversionedRESTClientFor(&config)
if err != nil {
return nil, err
}
return &Client{
restClient: restClient,
{{range .schemas}}
{{.ID}}Controllers: map[string]{{.CodeName}}Controller{},{{end}}
}, nil
}
func (c *Client) RESTClient() rest.Interface {
return c.restClient
}
{{range .schemas}}
type {{.CodeNamePlural}}Getter interface {
{{.CodeNamePlural}}(namespace string) {{.CodeName}}Interface
}
func (c *Client) {{.CodeNamePlural}}(namespace string) {{.CodeName}}Interface {
objectClient := clientbase.NewObjectClient(namespace, c.restClient, &{{.CodeName}}Resource, {{.CodeName}}GroupVersionKind, {{.ID}}Factory{})
return &{{.ID}}Client{
ns: namespace,
client: c,
objectClient: objectClient,
}
}
{{end}}
`

View File

@@ -61,13 +61,22 @@ func (c *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id stri
return nil, err return nil, err
} }
c.back(result.Object, schema) c.fromInternal(result.Object, schema)
return result.Object, nil return result.Object, nil
} }
func (c *Store) back(data map[string]interface{}, schema *types.Schema) { func (c *Store) toInternal(data map[string]interface{}, schema *types.Schema) {
//mapping.Metadata.Back(data) if schema.Mapper != nil {
schema.Mapper.ToInternal(data)
}
}
func (c *Store) fromInternal(data map[string]interface{}, schema *types.Schema) {
if schema.Mapper != nil {
schema.Mapper.FromInternal(data)
}
data["type"] = schema.ID data["type"] = schema.ID
name, _ := data["name"].(string) name, _ := data["name"].(string)
namespace, _ := data["namespace"].(string) namespace, _ := data["namespace"].(string)
@@ -79,6 +88,12 @@ func (c *Store) back(data map[string]interface{}, schema *types.Schema) {
data["id"] = namespace + ":" + name data["id"] = namespace + ":" + name
} }
} }
if status, ok := c.schemaStatus[schema]; ok {
if status.Spec.Scope != apiext.NamespaceScoped {
delete(data, "namespace")
}
}
} }
func (c *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) error { func (c *Store) Delete(apiContext *types.APIContext, schema *types.Schema, id string) error {
@@ -131,7 +146,7 @@ func (c *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *ty
result := []map[string]interface{}{} result := []map[string]interface{}{}
for _, obj := range resultList.Items { for _, obj := range resultList.Items {
c.back(obj.Object, schema) c.fromInternal(obj.Object, schema)
result = append(result, obj.Object) result = append(result, obj.Object)
} }
@@ -167,7 +182,8 @@ func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data
return nil, err return nil, err
} }
//mapping.Metadata.Forward(data) c.fromInternal(result.Object, schema)
for k, v := range data { for k, v := range data {
if k == "metadata" { if k == "metadata" {
continue continue
@@ -175,6 +191,8 @@ func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data
result.Object[k] = v result.Object[k] = v
} }
c.toInternal(result.Object, schema)
req = c.k8sClient.Put(). req = c.k8sClient.Put().
Prefix("apis", crd.Spec.Group, crd.Spec.Version). Prefix("apis", crd.Spec.Group, crd.Spec.Version).
Resource(crd.Status.AcceptedNames.Plural). Resource(crd.Status.AcceptedNames.Plural).
@@ -191,7 +209,7 @@ func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data
return nil, err return nil, err
} }
c.back(result.Object, schema) c.fromInternal(result.Object, schema)
return result.Object, nil return result.Object, nil
} }
@@ -208,6 +226,8 @@ func (c *Store) Create(apiContext *types.APIContext, schema *types.Schema, data
data["apiVersion"] = crd.Spec.Group + "/" + crd.Spec.Version data["apiVersion"] = crd.Spec.Group + "/" + crd.Spec.Version
data["kind"] = crd.Status.AcceptedNames.Kind data["kind"] = crd.Status.AcceptedNames.Kind
c.toInternal(data, schema)
req := c.k8sClient.Post(). req := c.k8sClient.Post().
Prefix("apis", crd.Spec.Group, crd.Spec.Version). Prefix("apis", crd.Spec.Group, crd.Spec.Version).
Body(&unstructured.Unstructured{ Body(&unstructured.Unstructured{
@@ -225,7 +245,7 @@ func (c *Store) Create(apiContext *types.APIContext, schema *types.Schema, data
return nil, err return nil, err
} }
c.back(result.Object, schema) c.fromInternal(result.Object, schema)
return result.Object, nil return result.Object, nil
} }

View File

@@ -48,7 +48,7 @@ func (t *TypeMapper) FromInternal(data map[string]interface{}) {
} }
func (t *TypeMapper) ToInternal(data map[string]interface{}) { func (t *TypeMapper) ToInternal(data map[string]interface{}) {
for i := len(t.Mappers) - 1; i <= 0; i-- { for i := len(t.Mappers) - 1; i >= 0; i-- {
t.Mappers[i].ToInternal(data) t.Mappers[i].ToInternal(data)
} }

View File

@@ -12,13 +12,15 @@ type Move struct {
} }
func (m Move) FromInternal(data map[string]interface{}) { func (m Move) FromInternal(data map[string]interface{}) {
if v, ok := GetValue(data, m.From); ok { if v, ok := data[m.From]; ok {
delete(data, m.From)
data[m.To] = v data[m.To] = v
} }
} }
func (m Move) ToInternal(data map[string]interface{}) { func (m Move) ToInternal(data map[string]interface{}) {
if v, ok := GetValue(data, m.To); ok { if v, ok := data[m.To]; ok {
delete(data, m.To)
data[m.From] = v data[m.From] = v
} }
} }

View File

@@ -0,0 +1,19 @@
package mapper
import "github.com/rancher/norman/types"
type Object struct {
types.TypeMapper
}
func NewObject(mappers []types.Mapper) *Object {
return &Object{
TypeMapper: types.TypeMapper{
Mappers: append(mappers,
&Drop{"status"},
&Embed{Field: "metadata"},
&Embed{Field: "spec"},
),
},
}
}

View File

@@ -55,6 +55,9 @@ func (s *Schemas) AddSchema(schema *Schema) *Schemas {
if schema.CodeName == "" { if schema.CodeName == "" {
schema.CodeName = convert.Capitalize(schema.ID) schema.CodeName = convert.Capitalize(schema.ID)
} }
if schema.CodeNamePlural == "" {
schema.CodeNamePlural = name.GuessPluralName(schema.CodeName)
}
schemas, ok := s.schemasByPath[schema.Version.Path] schemas, ok := s.schemasByPath[schema.Version.Path]
if !ok { if !ok {

View File

@@ -62,6 +62,7 @@ type APIVersion struct {
type Schema struct { type Schema struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
CodeName string `json:"-"` CodeName string `json:"-"`
CodeNamePlural string `json:"-"`
PkgName string `json:"-"` PkgName string `json:"-"`
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
Links map[string]string `json:"links"` Links map[string]string `json:"links"`