Skip to content
This repository has been archived by the owner on Mar 7, 2023. It is now read-only.

Commit

Permalink
chore: allow getting resources without version and group
Browse files Browse the repository at this point in the history
That should help to keep Theila backward and forward compatible with
any CRDs from CAPI or whatever else.

Now if the resource name is represented only with a single word without
any dots in it, the backend will look up version and group by using the
discovery service. Same way as it is done by kubectl.

Signed-off-by: Artem Chernyshev <artem.chernyshev@talos-systems.com>
  • Loading branch information
Unix4ever committed Apr 4, 2022
1 parent eb19087 commit d3c6004
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 35 deletions.
8 changes: 3 additions & 5 deletions frontend/src/api/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,13 @@ export const encodeProtobuf = (type?: string, spec?: any) => {
return res.encode(res.fromPartial(spec)).finish();
}

const capiVersion = "v1alpha4"

export const kubernetes = {
service: "services.v1",
pod: "pods.v1",
node: "nodes.v1",
cluster: `clusters.${capiVersion}.cluster.x-k8s.io`,
machine: `machines.${capiVersion}.cluster.x-k8s.io`,
sideroServers: "servers.v1alpha1.metal.sidero.dev",
cluster: `clusters`,
machine: `machines`,
sideroServers: "servers",
crd: "customresourcedefinitions.v1.apiextensions.k8s.io",
};

Expand Down
110 changes: 90 additions & 20 deletions internal/backend/runtime/kubernetes/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,27 @@ package kubernetes
import (
"context"
"fmt"
"net"
"strings"
"time"

"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/util/connrotation"
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
)

// Unstructured client wrapper.
type client struct {
client dynamic.Interface
Mapper meta.RESTMapper
client dynamic.Interface
clientset *kubernetes.Clientset
Mapper meta.RESTMapper
dialer *connrotation.Dialer
}

func (c *client) Resource(res *unstructured.Unstructured) (dynamic.ResourceInterface, error) {
Expand Down Expand Up @@ -114,12 +120,27 @@ func (c *client) Update(ctx context.Context, res *unstructured.Unstructured, opt
return dr.Update(ctx, res, opts, subresources...)
}

// Dynamic returns the underlying dynamic client.
func (c *client) Dynamic() dynamic.Interface {
return c.client
}

// Clientset returns the underlying clientset.
func (c *client) Clientset() *kubernetes.Clientset {
return c.clientset
}

// Close closes all clients.
func (c *client) Close() {
c.dialer.CloseAll()
}

func (c *client) kindFor(gvr schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return c.Mapper.KindFor(gvr)
}

func (c *client) parseResource(resource, namespace string) (*unstructured.Unstructured, error) {
gvr, err := getGVR(resource)
gvr, err := c.getGVR(resource)
if err != nil {
return nil, err
}
Expand All @@ -137,31 +158,25 @@ func (c *client) parseResource(resource, namespace string) (*unstructured.Unstru
return res, nil
}

func newClient(config *rest.Config) (*client, error) {
mapper, err := apiutil.NewDynamicRESTMapper(config)
if err != nil {
return nil, err
}

c, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}

return &client{c, mapper}, nil
}

func getGVR(resource string) (*schema.GroupVersionResource, error) {
func (c *client) getGVR(resource string) (*schema.GroupVersionResource, error) {
var gvr *schema.GroupVersionResource

parts := strings.Split(resource, ".")

if len(parts) == 2 {
var err error

switch {
case len(parts) == 2:
gvr = &schema.GroupVersionResource{
Resource: parts[0],
Version: parts[1],
}
} else {
case len(parts) == 1:
gvr, err = c.discoverGVR(resource)
if err != nil {
return nil, err
}
default:
gvr, _ = schema.ParseResourceArg(resource)
}

Expand All @@ -171,3 +186,58 @@ func getGVR(resource string) (*schema.GroupVersionResource, error) {

return gvr, nil
}

func (c *client) discoverGVR(resource string) (*schema.GroupVersionResource, error) {
gvr := &schema.GroupVersionResource{
Resource: resource,
}

resources, err := c.clientset.ServerPreferredResources()
if err != nil {
return nil, err
}

for _, res := range resources {
for _, r := range res.APIResources {
if r.Name == resource {
gv, err := schema.ParseGroupVersion(res.GroupVersion)
if err != nil {
return nil, err
}

gvr.Version = gv.Version
gvr.Group = gv.Group
}
}
}

return gvr, nil
}

func newClient(config *rest.Config) (*client, error) {
mapper, err := apiutil.NewDynamicRESTMapper(config)
if err != nil {
return nil, err
}

if config.Timeout == 0 {
config.Timeout = 30 * time.Second
}

dialer := connrotation.NewDialer((&net.Dialer{Timeout: 30 * time.Second, KeepAlive: 30 * time.Second}).DialContext)
config.Dial = dialer.DialContext

c, err := dynamic.NewForConfig(config)
if err != nil {
return nil, err
}

var clientset *kubernetes.Clientset

clientset, err = kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}

return &client{c, clientset, mapper, dialer}, nil
}
28 changes: 18 additions & 10 deletions internal/backend/runtime/kubernetes/kubernetes.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/dynamic/dynamicinformer"
"k8s.io/client-go/rest"
toolscache "k8s.io/client-go/tools/cache"
Expand Down Expand Up @@ -81,7 +80,21 @@ func (r *Runtime) Watch(ctx context.Context, request *message.WatchSpec, events
cancel: cancel,
}

if err := w.run(ctx); err != nil {
opts := []runtime.QueryOption{}
if request.Context != nil {
opts = append(opts, runtime.WithContext(request.Context.Name))

if request.Context.Cluster != nil {
opts = append(opts, runtime.WithCluster(request.Context.Cluster))
}
}

client, err := r.getOrCreateClient(ctx, runtime.NewQueryOptions(opts...))
if err != nil {
return err
}

if err := w.run(ctx, client); err != nil {
cancel()

return err
Expand Down Expand Up @@ -394,12 +407,7 @@ type Watch struct {
selector string
}

func (w *Watch) run(ctx context.Context) error {
dc, err := dynamic.NewForConfig(w.config)
if err != nil {
return err
}

func (w *Watch) run(ctx context.Context, client *client) error {
namespace := v1.NamespaceAll
if w.resource.Namespace != "" {
namespace = w.resource.Namespace
Expand All @@ -412,9 +420,9 @@ func (w *Watch) run(ctx context.Context) error {
}
}

dynamicInformer := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dc, 0, namespace, filter)
dynamicInformer := dynamicinformer.NewFilteredDynamicSharedInformerFactory(client.Dynamic(), 0, namespace, filter)

gvr, err := getGVR(w.resource.Type)
gvr, err := client.getGVR(w.resource.Type)
if err != nil {
return err
}
Expand Down

0 comments on commit d3c6004

Please sign in to comment.