mirror of
https://github.com/rancher/norman.git
synced 2025-06-21 13:07:10 +00:00
185 lines
4.6 KiB
Go
185 lines
4.6 KiB
Go
|
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)
|
||
|
}
|