diff --git a/clientbase/object_client.go b/clientbase/object_client.go index 32e7558b..23443478 100644 --- a/clientbase/object_client.go +++ b/clientbase/object_client.go @@ -25,24 +25,14 @@ type ObjectClient struct { Factory ObjectFactory } -func NewObjectClient(namespace string, config rest.Config, apiResource *metav1.APIResource, gvk schema.GroupVersionKind, factory ObjectFactory) (*ObjectClient, error) { - if config.NegotiatedSerializer == nil { - configConfig := dynamic.ContentConfig() - config.NegotiatedSerializer = configConfig.NegotiatedSerializer - } - - restClient, err := rest.UnversionedRESTClientFor(&config) - if err != nil { - return nil, err - } - +func NewObjectClient(namespace string, restClient rest.Interface, apiResource *metav1.APIResource, gvk schema.GroupVersionKind, factory ObjectFactory) *ObjectClient { return &ObjectClient{ restClient: restClient, resource: apiResource, gvk: gvk, ns: namespace, Factory: factory, - }, nil + } } func (p *ObjectClient) Create(o runtime.Object) (runtime.Object, error) { diff --git a/controller/generic_controller.go b/controller/generic_controller.go index 4ebe911d..9d41235e 100644 --- a/controller/generic_controller.go +++ b/controller/generic_controller.go @@ -37,7 +37,7 @@ type genericController struct { running bool } -func NewGenericController(name string, objectClient *clientbase.ObjectClient) (GenericController, error) { +func NewGenericController(name string, objectClient *clientbase.ObjectClient) GenericController { informer := cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: objectClient.List, @@ -50,7 +50,7 @@ func NewGenericController(name string, objectClient *clientbase.ObjectClient) (G queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), name), name: name, - }, nil + } } func (g *genericController) Informer() cache.SharedIndexInformer { diff --git a/generator/controller_template.go b/generator/controller_template.go index fd82b1d9..6cdca71e 100644 --- a/generator/controller_template.go +++ b/generator/controller_template.go @@ -54,7 +54,7 @@ type {{.schema.CodeName}}Interface interface { List(opts metav1.ListOptions) (*{{.schema.CodeName}}List, error) Watch(opts metav1.ListOptions) (watch.Interface, error) DeleteCollection(deleteOpts *metav1.DeleteOptions, listOpts metav1.ListOptions) error - Controller() ({{.schema.CodeName}}Controller, error) + Controller() {{.schema.CodeName}}Controller } type {{.schema.ID}}Controller struct { @@ -85,35 +85,30 @@ func (c {{.schema.ID}}Factory) List() runtime.Object { return &{{.schema.CodeName}}List{} } -func New{{.schema.CodeName}}Client(namespace string, config rest.Config) ({{.schema.CodeName}}Interface, error) { - objectClient, err := clientbase.NewObjectClient(namespace, config, &{{.schema.CodeName}}Resource, {{.schema.CodeName}}GroupVersionKind, {{.schema.ID}}Factory{}) - return &{{.schema.ID}}Client{ - objectClient: objectClient, - }, err -} +func (s *{{.schema.ID}}Client) Controller() {{.schema.CodeName}}Controller { + s.client.Lock() + defer s.client.Unlock() -func (s *{{.schema.ID}}Client) Controller() ({{.schema.CodeName}}Controller, error) { - s.Lock() - defer s.Unlock() - - if s.controller != nil { - return s.controller, nil + c, ok := s.client.{{.schema.ID}}Controllers[s.ns] + if ok { + return c } - controller, err := controller.NewGenericController({{.schema.CodeName}}GroupVersionKind.Kind+"Controller", + genericController := controller.NewGenericController({{.schema.CodeName}}GroupVersionKind.Kind+"Controller", s.objectClient) - if err != nil { - return nil, err + + c = &{{.schema.ID}}Controller{ + GenericController: genericController, } - s.controller = &{{.schema.ID}}Controller{ - GenericController: controller, - } - return s.controller, nil + s.client.{{.schema.ID}}Controllers[s.ns] = c + + return c } type {{.schema.ID}}Client struct { - sync.Mutex + client *Client + ns string objectClient *clientbase.ObjectClient controller {{.schema.CodeName}}Controller } diff --git a/generator/generator.go b/generator/generator.go index ea905554..ba58244a 100644 --- a/generator/generator.go +++ b/generator/generator.go @@ -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 { template, err := template.New("client.template"). Funcs(funcs()). @@ -177,7 +198,7 @@ func Generate(schemas *types.Schemas, cattleOutputPackage, k8sOutputPackage stri return err } - doDeepCopy := false + controllers := []*types.Schema{} generated := []*types.Schema{} for _, schema := range schemas.Schemas() { @@ -192,7 +213,7 @@ func Generate(schemas *types.Schemas, cattleOutputPackage, k8sOutputPackage stri if contains(schema.CollectionMethods, http.MethodGet) && !strings.HasPrefix(schema.PkgName, "k8s.io") && !strings.Contains(schema.PkgName, "/vendor/") { - doDeepCopy = true + controllers = append(controllers, schema) if err := generateController(k8sDir, schema, schemas); err != nil { return err } @@ -205,10 +226,12 @@ func Generate(schemas *types.Schemas, cattleOutputPackage, k8sOutputPackage stri return err } - if doDeepCopy { + if len(controllers) > 0 { if err := deepCopyGen(baseDir, k8sOutputPackage); err != nil { return err } + + generateK8sClient(k8sDir, &controllers[0].Version, controllers) } if err := gofmt(baseDir, k8sOutputPackage); err != nil { diff --git a/generator/k8s_client_template.go b/generator/k8s_client_template.go new file mode 100644 index 00000000..3dc1770d --- /dev/null +++ b/generator/k8s_client_template.go @@ -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}} +` diff --git a/store/crd/crd_store.go b/store/crd/crd_store.go index 3d4404d5..58bcb342 100644 --- a/store/crd/crd_store.go +++ b/store/crd/crd_store.go @@ -61,13 +61,22 @@ func (c *Store) ByID(apiContext *types.APIContext, schema *types.Schema, id stri return nil, err } - c.back(result.Object, schema) + c.fromInternal(result.Object, schema) return result.Object, nil } -func (c *Store) back(data map[string]interface{}, schema *types.Schema) { - //mapping.Metadata.Back(data) +func (c *Store) toInternal(data map[string]interface{}, schema *types.Schema) { + 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 name, _ := data["name"].(string) namespace, _ := data["namespace"].(string) @@ -79,6 +88,12 @@ func (c *Store) back(data map[string]interface{}, schema *types.Schema) { 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 { @@ -131,7 +146,7 @@ func (c *Store) List(apiContext *types.APIContext, schema *types.Schema, opt *ty result := []map[string]interface{}{} for _, obj := range resultList.Items { - c.back(obj.Object, schema) + c.fromInternal(obj.Object, schema) result = append(result, obj.Object) } @@ -167,7 +182,8 @@ func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data return nil, err } - //mapping.Metadata.Forward(data) + c.fromInternal(result.Object, schema) + for k, v := range data { if k == "metadata" { continue @@ -175,6 +191,8 @@ func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data result.Object[k] = v } + c.toInternal(result.Object, schema) + req = c.k8sClient.Put(). Prefix("apis", crd.Spec.Group, crd.Spec.Version). Resource(crd.Status.AcceptedNames.Plural). @@ -191,7 +209,7 @@ func (c *Store) Update(apiContext *types.APIContext, schema *types.Schema, data return nil, err } - c.back(result.Object, schema) + c.fromInternal(result.Object, schema) 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["kind"] = crd.Status.AcceptedNames.Kind + c.toInternal(data, schema) + req := c.k8sClient.Post(). Prefix("apis", crd.Spec.Group, crd.Spec.Version). Body(&unstructured.Unstructured{ @@ -225,7 +245,7 @@ func (c *Store) Create(apiContext *types.APIContext, schema *types.Schema, data return nil, err } - c.back(result.Object, schema) + c.fromInternal(result.Object, schema) return result.Object, nil } diff --git a/types/mapper.go b/types/mapper.go index ba99c363..0b43e6c0 100644 --- a/types/mapper.go +++ b/types/mapper.go @@ -48,7 +48,7 @@ func (t *TypeMapper) FromInternal(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) } diff --git a/types/mapping/mapper/move.go b/types/mapping/mapper/move.go index 6c7ba333..c47d29eb 100644 --- a/types/mapping/mapper/move.go +++ b/types/mapping/mapper/move.go @@ -12,13 +12,15 @@ type Move struct { } 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 } } 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 } } diff --git a/types/mapping/mapper/object.go b/types/mapping/mapper/object.go new file mode 100644 index 00000000..0c5f8f71 --- /dev/null +++ b/types/mapping/mapper/object.go @@ -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"}, + ), + }, + } +} diff --git a/types/schemas.go b/types/schemas.go index 949ffcdc..f7a69054 100644 --- a/types/schemas.go +++ b/types/schemas.go @@ -55,6 +55,9 @@ func (s *Schemas) AddSchema(schema *Schema) *Schemas { if schema.CodeName == "" { schema.CodeName = convert.Capitalize(schema.ID) } + if schema.CodeNamePlural == "" { + schema.CodeNamePlural = name.GuessPluralName(schema.CodeName) + } schemas, ok := s.schemasByPath[schema.Version.Path] if !ok { diff --git a/types/types.go b/types/types.go index 56951606..36eddca1 100644 --- a/types/types.go +++ b/types/types.go @@ -62,6 +62,7 @@ type APIVersion struct { type Schema struct { ID string `json:"id,omitempty"` CodeName string `json:"-"` + CodeNamePlural string `json:"-"` PkgName string `json:"-"` Type string `json:"type,omitempty"` Links map[string]string `json:"links"`