package clientbase import ( "encoding/json" "github.com/pkg/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" ) type ObjectFactory interface { Object() runtime.Object List() runtime.Object } type ObjectClient struct { restClient rest.Interface resource *metav1.APIResource gvk schema.GroupVersionKind ns string 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 } return &ObjectClient{ restClient: restClient, resource: apiResource, gvk: gvk, ns: namespace, Factory: factory, }, nil } func (p *ObjectClient) Create(o runtime.Object) (runtime.Object, error) { ns := p.ns if obj, ok := o.(metav1.Object); ok && obj.GetNamespace() != "" { ns = obj.GetNamespace() } result := p.Factory.Object() err := p.restClient.Post(). Prefix("apis", p.gvk.Group, p.gvk.Version). NamespaceIfScoped(ns, p.resource.Namespaced). Resource(p.resource.Name). Body(o). Do(). Into(result) return result, err } func (p *ObjectClient) Get(name string, opts metav1.GetOptions) (runtime.Object, error) { result := p.Factory.Object() err := p.restClient.Get(). Prefix("apis", p.gvk.Group, p.gvk.Version). NamespaceIfScoped(p.ns, p.resource.Namespaced). Resource(p.resource.Name). VersionedParams(&opts, dynamic.VersionedParameterEncoderWithV1Fallback). Name(name). Do(). Into(result) return result, err } func (p *ObjectClient) Update(name string, o runtime.Object) (runtime.Object, error) { ns := p.ns if obj, ok := o.(metav1.Object); ok && obj.GetNamespace() != "" { ns = obj.GetNamespace() } result := p.Factory.Object() if len(name) == 0 { return result, errors.New("object missing name") } err := p.restClient.Put(). Prefix("apis", p.gvk.Group, p.gvk.Version). NamespaceIfScoped(ns, p.resource.Namespaced). Resource(p.resource.Name). Name(name). Body(o). Do(). Into(result) return result, err } func (p *ObjectClient) Delete(name string, opts *metav1.DeleteOptions) error { return p.restClient.Delete(). Prefix("apis", p.gvk.Group, p.gvk.Version). NamespaceIfScoped(p.ns, p.resource.Namespaced). Resource(p.resource.Name). Name(name). Body(opts). Do(). Error() } func (p *ObjectClient) List(opts metav1.ListOptions) (runtime.Object, error) { result := p.Factory.List() return result, p.restClient.Get(). Prefix("apis", p.gvk.Group, p.gvk.Version). NamespaceIfScoped(p.ns, p.resource.Namespaced). Resource(p.resource.Name). VersionedParams(&opts, dynamic.VersionedParameterEncoderWithV1Fallback). Do(). Into(result) } func (p *ObjectClient) Watch(opts metav1.ListOptions) (watch.Interface, error) { r, err := p.restClient.Get(). Prefix("apis", p.gvk.Group, p.gvk.Version). Prefix("watch"). Namespace(p.ns). NamespaceIfScoped(p.ns, p.resource.Namespaced). Resource(p.resource.Name). VersionedParams(&opts, dynamic.VersionedParameterEncoderWithV1Fallback). Stream() if err != nil { return nil, err } return watch.NewStreamWatcher(&dynamicDecoder{ factory: p.Factory, dec: json.NewDecoder(r), close: r.Close, }), nil } func (p *ObjectClient) DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error { return p.restClient.Delete(). Prefix("apis", p.gvk.Group, p.gvk.Version). NamespaceIfScoped(p.ns, p.resource.Namespaced). Resource(p.resource.Name). VersionedParams(&listOptions, dynamic.VersionedParameterEncoderWithV1Fallback). Body(deleteOptions). Do(). Error() } type dynamicDecoder struct { factory ObjectFactory dec *json.Decoder close func() error } func (d *dynamicDecoder) Close() { d.close() } func (d *dynamicDecoder) Decode() (action watch.EventType, object runtime.Object, err error) { e := dynamicEvent{ Object: holder{ factory: d.factory, }, } if err := d.dec.Decode(&e); err != nil { return watch.Error, nil, err } return e.Type, e.Object.obj, nil } type dynamicEvent struct { Type watch.EventType Object holder } type holder struct { factory ObjectFactory obj runtime.Object } func (h *holder) UnmarshalJSON(b []byte) error { h.obj = h.factory.Object() return json.Unmarshal(b, h.obj) }