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:
@@ -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) {
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
62
generator/k8s_client_template.go
Normal file
62
generator/k8s_client_template.go
Normal 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}}
|
||||||
|
`
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
19
types/mapping/mapper/object.go
Normal file
19
types/mapping/mapper/object.go
Normal 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"},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@@ -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 {
|
||||||
|
@@ -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"`
|
||||||
|
Reference in New Issue
Block a user