1
0
mirror of https://github.com/rancher/norman.git synced 2025-04-29 20:04:03 +00:00
norman/objectclient/object_client.go

333 lines
10 KiB
Go
Raw Normal View History

2018-04-03 19:49:20 +00:00
package objectclient
2017-11-11 04:44:02 +00:00
import (
"encoding/json"
2019-09-25 21:32:42 +00:00
"net/http"
"strings"
2017-11-11 04:44:02 +00:00
"github.com/pkg/errors"
2018-04-02 22:45:10 +00:00
"github.com/rancher/norman/restwatch"
2018-03-23 16:39:25 +00:00
"github.com/sirupsen/logrus"
2019-09-25 21:32:42 +00:00
k8sError "k8s.io/apimachinery/pkg/api/errors"
2017-12-05 16:21:12 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2017-11-11 04:44:02 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2017-12-16 08:25:56 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2017-11-11 04:44:02 +00:00
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
json2 "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
2017-12-23 06:09:40 +00:00
"k8s.io/apimachinery/pkg/types"
2017-11-11 04:44:02 +00:00
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/rest"
restclientwatch "k8s.io/client-go/rest/watch"
2017-11-11 04:44:02 +00:00
)
type ObjectFactory interface {
Object() runtime.Object
List() runtime.Object
}
2017-12-16 08:25:56 +00:00
type UnstructuredObjectFactory struct {
}
func (u *UnstructuredObjectFactory) Object() runtime.Object {
return &unstructured.Unstructured{}
}
func (u *UnstructuredObjectFactory) List() runtime.Object {
return &unstructured.UnstructuredList{}
}
type GenericClient interface {
UnstructuredClient() GenericClient
GroupVersionKind() schema.GroupVersionKind
Create(o runtime.Object) (runtime.Object, error)
GetNamespaced(namespace, name string, opts metav1.GetOptions) (runtime.Object, error)
Get(name string, opts metav1.GetOptions) (runtime.Object, error)
Update(name string, o runtime.Object) (runtime.Object, error)
DeleteNamespaced(namespace, name string, opts *metav1.DeleteOptions) error
Delete(name string, opts *metav1.DeleteOptions) error
List(opts metav1.ListOptions) (runtime.Object, error)
ListNamespaced(namespace string, opts metav1.ListOptions) (runtime.Object, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Patch(name string, o runtime.Object, patchType types.PatchType, data []byte, subresources ...string) (runtime.Object, error)
ObjectFactory() ObjectFactory
}
2017-11-11 04:44:02 +00:00
type ObjectClient struct {
restClient rest.Interface
resource *metav1.APIResource
gvk schema.GroupVersionKind
ns string
Factory ObjectFactory
}
2017-11-13 19:50:25 +00:00
func NewObjectClient(namespace string, restClient rest.Interface, apiResource *metav1.APIResource, gvk schema.GroupVersionKind, factory ObjectFactory) *ObjectClient {
2017-11-11 04:44:02 +00:00
return &ObjectClient{
restClient: restClient,
resource: apiResource,
gvk: gvk,
ns: namespace,
Factory: factory,
2017-11-13 19:50:25 +00:00
}
2017-11-11 04:44:02 +00:00
}
func (p *ObjectClient) UnstructuredClient() GenericClient {
2017-12-16 08:25:56 +00:00
return &ObjectClient{
restClient: p.restClient,
resource: p.resource,
gvk: p.gvk,
ns: p.ns,
Factory: &UnstructuredObjectFactory{},
}
}
2017-12-20 04:39:57 +00:00
func (p *ObjectClient) GroupVersionKind() schema.GroupVersionKind {
return p.gvk
}
2017-11-14 20:46:34 +00:00
func (p *ObjectClient) getAPIPrefix() string {
if p.gvk.Group == "" {
return "api"
}
return "apis"
}
2017-11-11 04:44:02 +00:00
func (p *ObjectClient) Create(o runtime.Object) (runtime.Object, error) {
ns := p.ns
obj, ok := o.(metav1.Object)
if ok && obj.GetNamespace() != "" {
2017-11-11 04:44:02 +00:00
ns = obj.GetNamespace()
}
if ok {
labels := obj.GetLabels()
if labels == nil {
labels = make(map[string]string)
} else {
ls := make(map[string]string)
for k, v := range labels {
ls[k] = v
}
labels = ls
}
labels["cattle.io/creator"] = "norman"
obj.SetLabels(labels)
}
2017-12-05 16:21:12 +00:00
if t, err := meta.TypeAccessor(o); err == nil {
if t.GetKind() == "" {
t.SetKind(p.gvk.Kind)
}
if t.GetAPIVersion() == "" {
apiVersion, _ := p.gvk.ToAPIVersionAndKind()
t.SetAPIVersion(apiVersion)
}
}
2017-11-11 04:44:02 +00:00
result := p.Factory.Object()
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST CREATE %s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, ns, p.resource.Name)
2017-11-11 04:44:02 +00:00
err := p.restClient.Post().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
NamespaceIfScoped(ns, p.resource.Namespaced).
Resource(p.resource.Name).
Body(o).
Do().
Into(result)
return result, err
}
2018-01-18 23:22:36 +00:00
func (p *ObjectClient) GetNamespaced(namespace, name string, opts metav1.GetOptions) (runtime.Object, error) {
2017-12-20 04:39:57 +00:00
result := p.Factory.Object()
req := p.restClient.Get().
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version)
if namespace != "" {
req = req.Namespace(namespace)
}
2018-01-12 10:12:12 +00:00
err := req.
2017-12-20 04:39:57 +00:00
Resource(p.resource.Name).
2018-10-10 02:27:02 +00:00
VersionedParams(&opts, metav1.ParameterCodec).
2017-12-20 04:39:57 +00:00
Name(name).
Do().
Into(result)
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST GET %s/%s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, namespace, p.resource.Name, name)
2017-12-20 04:39:57 +00:00
return result, err
}
2017-11-11 04:44:02 +00:00
func (p *ObjectClient) Get(name string, opts metav1.GetOptions) (runtime.Object, error) {
result := p.Factory.Object()
err := p.restClient.Get().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
NamespaceIfScoped(p.ns, p.resource.Namespaced).
Resource(p.resource.Name).
2018-10-10 02:27:02 +00:00
VersionedParams(&opts, metav1.ParameterCodec).
2017-11-11 04:44:02 +00:00
Name(name).
Do().
Into(result)
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST GET %s/%s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, p.ns, p.resource.Name, name)
2017-11-11 04:44:02 +00:00
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")
}
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST UPDATE %s/%s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, ns, p.resource.Name, name)
2017-11-11 04:44:02 +00:00
err := p.restClient.Put().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
NamespaceIfScoped(ns, p.resource.Namespaced).
Resource(p.resource.Name).
Name(name).
Body(o).
Do().
Into(result)
return result, err
}
2018-01-18 23:22:36 +00:00
func (p *ObjectClient) DeleteNamespaced(namespace, name string, opts *metav1.DeleteOptions) error {
2017-12-20 04:39:57 +00:00
req := p.restClient.Delete().
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version)
if namespace != "" {
req = req.Namespace(namespace)
}
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST DELETE %s/%s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, namespace, p.resource.Name, name)
2017-12-20 04:39:57 +00:00
return req.Resource(p.resource.Name).
Name(name).
Body(opts).
Do().
Error()
}
2017-11-11 04:44:02 +00:00
func (p *ObjectClient) Delete(name string, opts *metav1.DeleteOptions) error {
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST DELETE %s/%s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, p.ns, p.resource.Name, name)
2017-11-11 04:44:02 +00:00
return p.restClient.Delete().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
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()
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST LIST %s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, p.ns, p.resource.Name)
2017-11-11 04:44:02 +00:00
return result, p.restClient.Get().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
NamespaceIfScoped(p.ns, p.resource.Namespaced).
Resource(p.resource.Name).
2018-10-10 02:27:02 +00:00
VersionedParams(&opts, metav1.ParameterCodec).
2017-11-11 04:44:02 +00:00
Do().
Into(result)
}
func (p *ObjectClient) ListNamespaced(namespace string, opts metav1.ListOptions) (runtime.Object, error) {
result := p.Factory.List()
2020-03-11 21:48:52 +00:00
logrus.Tracef("REST LIST %s/%s/%s/%s/%s", p.getAPIPrefix(), p.gvk.Group, p.gvk.Version, namespace, p.resource.Name)
return result, p.restClient.Get().
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
NamespaceIfScoped(namespace, p.resource.Namespaced).
Resource(p.resource.Name).
VersionedParams(&opts, metav1.ParameterCodec).
Do().
Into(result)
}
2017-11-11 04:44:02 +00:00
func (p *ObjectClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
2018-04-02 22:45:10 +00:00
restClient := p.restClient
if watchClient, ok := restClient.(restwatch.WatchClient); ok {
restClient = watchClient.WatchClient()
}
r, err := restClient.Get().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
Prefix("watch").
NamespaceIfScoped(p.ns, p.resource.Namespaced).
Resource(p.resource.Name).
2018-10-10 02:27:02 +00:00
VersionedParams(&opts, metav1.ParameterCodec).
2017-11-11 04:44:02 +00:00
Stream()
if err != nil {
return nil, err
}
embeddedDecoder := &structuredDecoder{
2017-11-11 04:44:02 +00:00
factory: p.Factory,
}
streamDecoder := streaming.NewDecoder(json2.Framer.NewFrameReader(r), embeddedDecoder)
decoder := restclientwatch.NewDecoder(streamDecoder, embeddedDecoder)
2019-09-25 21:32:42 +00:00
return watch.NewStreamWatcher(decoder, k8sError.NewClientErrorReporter(http.StatusInternalServerError, "watch", "ClientWatchDecoding")), nil
}
type structuredDecoder struct {
factory ObjectFactory
}
func (d *structuredDecoder) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
if into == nil {
into = d.factory.Object()
}
err := json.Unmarshal(data, &into)
if err != nil {
status := &metav1.Status{}
if err := json.Unmarshal(data, status); err == nil && strings.ToLower(status.Kind) == "status" {
return status, defaults, nil
}
return nil, nil, err
}
if _, ok := into.(*metav1.Status); !ok && strings.ToLower(into.GetObjectKind().GroupVersionKind().Kind) == "status" {
into = &metav1.Status{}
err := json.Unmarshal(data, into)
if err != nil {
return nil, nil, err
}
}
return into, defaults, err
2017-11-11 04:44:02 +00:00
}
func (p *ObjectClient) DeleteCollection(deleteOptions *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
return p.restClient.Delete().
2017-11-14 20:46:34 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
2017-11-11 04:44:02 +00:00
NamespaceIfScoped(p.ns, p.resource.Namespaced).
Resource(p.resource.Name).
2018-10-10 02:27:02 +00:00
VersionedParams(&listOptions, metav1.ParameterCodec).
2017-11-11 04:44:02 +00:00
Body(deleteOptions).
Do().
Error()
}
func (p *ObjectClient) Patch(name string, o runtime.Object, patchType types.PatchType, data []byte, subresources ...string) (runtime.Object, error) {
2017-12-23 06:09:40 +00:00
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.Patch(patchType).
2017-12-23 06:09:40 +00:00
Prefix(p.getAPIPrefix(), p.gvk.Group, p.gvk.Version).
NamespaceIfScoped(ns, p.resource.Namespaced).
Resource(p.resource.Name).
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return result, err
}
func (p *ObjectClient) ObjectFactory() ObjectFactory {
return p.Factory
}