Skip to content

Commit d32ee95

Browse files
committed
debug
1 parent 3baf1a4 commit d32ee95

9 files changed

Lines changed: 311 additions & 69 deletions

File tree

backend/http/handler.go

Lines changed: 137 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package http
1818

1919
import (
2020
"bytes"
21+
"context"
2122
"encoding/base64"
2223
"encoding/json"
2324
"errors"
@@ -33,9 +34,12 @@ import (
3334
"github.com/gorilla/securecookie"
3435
"golang.org/x/oauth2"
3536
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
37+
apierrors "k8s.io/apimachinery/pkg/api/errors"
3638
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
39+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
3740
"k8s.io/apimachinery/pkg/labels"
3841
"k8s.io/apimachinery/pkg/runtime"
42+
"k8s.io/apimachinery/pkg/runtime/schema"
3943
componentbaseversion "k8s.io/component-base/version"
4044
"k8s.io/klog/v2"
4145

@@ -66,6 +70,7 @@ type handler struct {
6670
backendCallbackURL string
6771
providerPrettyName string
6872
testingAutoSelect string
73+
schemaSource string
6974

7075
cookieEncryptionKey []byte
7176
cookieSigningKey []byte
@@ -78,6 +83,7 @@ func NewHandler(
7883
provider *OIDCServiceProvider,
7984
oidcAuthorizeURL, backendCallbackURL, providerPrettyName, testingAutoSelect string,
8085
cookieSigningKey, cookieEncryptionKey []byte,
86+
schemaSource string,
8187
scope kubebindv1alpha2.InformerScope,
8288
mgr *kubernetes.Manager,
8389
) (*handler, error) {
@@ -87,6 +93,7 @@ func NewHandler(
8793
backendCallbackURL: backendCallbackURL,
8894
providerPrettyName: providerPrettyName,
8995
testingAutoSelect: testingAutoSelect,
96+
schemaSource: schemaSource,
9097
scope: scope,
9198
client: http.DefaultClient,
9299
kubeManager: mgr,
@@ -328,6 +335,15 @@ func createSessionState(authCode *AuthCode, token *oauth2.Token) (*session.State
328335
}, nil
329336
}
330337

338+
type UISchema struct {
339+
Name string
340+
Version string
341+
Group string
342+
Kind string
343+
Scope string // "Namespaced" or "Cluster"
344+
Resource string
345+
}
346+
331347
func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) {
332348
logger := getLogger(r)
333349

@@ -346,41 +362,74 @@ func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) {
346362
return
347363
}
348364

349-
labelSelector := labels.Set{
350-
resources.ExportedCRDsLabel: "true",
351-
}
352-
353-
crds, err := h.kubeManager.ListCustomResourceDefinitions(r.Context(), cluster, labelSelector.AsSelector())
365+
apiResourceSchemas, err := h.getBackendDynamicResource(r.Context(), cluster)
354366
if err != nil {
355-
logger.Error(err, "failed to list crds")
367+
logger.Error(err, "failed to get dynamic resources")
356368
http.Error(w, "internal error", http.StatusInternalServerError)
357369
return
358370
}
359-
sort.SliceStable(crds.Items, func(i, j int) bool {
360-
return crds.Items[i].Name < crds.Items[j].Name
361-
})
362-
rightScopedCRDs := []*apiextensionsv1.CustomResourceDefinition{}
363-
for _, crd := range crds.Items {
364-
if h.scope == kubebindv1alpha2.ClusterScope || crd.Spec.Scope == apiextensionsv1.NamespaceScoped {
365-
rightScopedCRDs = append(rightScopedCRDs, &crd)
371+
372+
var result []UISchema
373+
for _, item := range apiResourceSchemas.Items {
374+
scope := item.UnstructuredContent()["spec"].(map[string]interface{})["scope"]
375+
if scope == nil {
376+
scope = "-"
366377
}
367-
}
368378

369-
apiResourceSchemas, err := h.kubeManager.ListAPIResourceSchemas(r.Context(), cluster)
370-
if err != nil {
371-
logger.Error(err, "failed to get api resource schemas")
372-
http.Error(w, "internal error", http.StatusInternalServerError)
373-
return
379+
// Convert scope to string for comparison
380+
scopeStr := scope.(string)
381+
handlerScopeStr := string(h.scope)
382+
383+
// Skip resources that don't match the handler's scope configuration
384+
// If handler is configured for ClusterScope, only show cluster-scoped resources
385+
// If handler is configured for NamespacedScope, only show namespaced resources
386+
if (handlerScopeStr == string(kubebindv1alpha2.ClusterScope) && scopeStr == string(apiextensionsv1.NamespaceScoped)) ||
387+
(handlerScopeStr == string(kubebindv1alpha2.NamespacedScope) && scopeStr == string(apiextensionsv1.ClusterScoped)) {
388+
continue
389+
}
390+
391+
group := item.UnstructuredContent()["spec"].(map[string]interface{})["group"]
392+
if group == nil {
393+
group = "-"
394+
}
395+
resource := item.UnstructuredContent()["spec"].(map[string]interface{})["names"].(map[string]interface{})["plural"]
396+
if resource == nil {
397+
resource = "-"
398+
}
399+
400+
kind := item.UnstructuredContent()["spec"].(map[string]interface{})["names"].(map[string]interface{})["kind"]
401+
if kind == nil {
402+
kind = "-"
403+
}
404+
namespace := item.GetNamespace()
405+
if namespace == "" {
406+
namespace = "-"
407+
}
408+
409+
versions := item.UnstructuredContent()["spec"].(map[string]interface{})["versions"]
410+
if versions == nil {
411+
versions = []interface{}{""}
412+
}
413+
for _, v := range versions.([]interface{}) {
414+
version := v.(map[string]interface{})["name"]
415+
result = append(result, UISchema{
416+
Name: item.GetName(),
417+
Kind: kind.(string),
418+
Scope: scope.(string),
419+
Version: version.(string),
420+
Group: group.(string),
421+
Resource: resource.(string),
422+
})
423+
}
374424
}
425+
375426
bs := bytes.Buffer{}
376427
if err := resourcesTemplate.Execute(&bs, struct {
377-
Cluster string
378-
CRDs []*apiextensionsv1.CustomResourceDefinition
379-
APIResourceSchemas []kubebindv1alpha2.APIResourceSchema
428+
Cluster string
429+
Schemas []UISchema
380430
}{
381-
Cluster: cluster,
382-
CRDs: rightScopedCRDs,
383-
APIResourceSchemas: apiResourceSchemas.Items,
431+
Cluster: cluster,
432+
Schemas: result,
384433
}); err != nil {
385434
logger.Error(err, "failed to execute template")
386435
http.Error(w, "internal error", http.StatusInternalServerError)
@@ -393,8 +442,10 @@ func (h *handler) handleResources(w http.ResponseWriter, r *http.Request) {
393442

394443
func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) {
395444
logger := getLogger(r)
445+
name := r.URL.Query().Get("name")
396446
group := r.URL.Query().Get("group")
397447
resource := r.URL.Query().Get("resource")
448+
version := r.URL.Query().Get("version")
398449
cluster := mux.Vars(r)["cluster"]
399450

400451
prepareNoCache(w)
@@ -415,7 +466,38 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) {
415466
return
416467
}
417468

418-
kfg, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject+"#"+state.ClusterID, cluster, resource, group)
469+
// There is an intent to bind. We need to create APIResourceSchema if one does not exists.
470+
{
471+
apiResourceSchemas, err := h.getBackendDynamicResource(r.Context(), cluster)
472+
if err != nil {
473+
logger.Error(err, "failed to get dynamic resources")
474+
http.Error(w, "internal error", http.StatusInternalServerError)
475+
return
476+
}
477+
478+
schema := &unstructured.Unstructured{}
479+
for _, item := range apiResourceSchemas.Items {
480+
if item.GetName() == name {
481+
schema = &item
482+
break
483+
}
484+
}
485+
if schema == nil || schema.GetName() != name {
486+
logger.Error(nil, "no APIResourceSchema found", "name", name, "group", group, "resource", resource, "version", version)
487+
http.Error(w, fmt.Sprintf("no APIResourceSchema found for %s.%s.%s/%s", group, resource, version, name), http.StatusNotFound)
488+
return
489+
}
490+
491+
// create apiResourceSchema if not exists
492+
err = h.kubeManager.CreateAPIResourceSchema(r.Context(), cluster, name, schema)
493+
if err != nil && !apierrors.IsAlreadyExists(err) {
494+
logger.Error(err, "failed to create APIResourceSchema")
495+
http.Error(w, "internal error", http.StatusInternalServerError)
496+
return
497+
}
498+
}
499+
500+
kfg, err := h.kubeManager.HandleResources(r.Context(), state.Token.Subject+"#"+state.ClusterID, cluster)
419501
if err != nil {
420502
logger.Error(err, "failed to handle resources")
421503
http.Error(w, "internal error", http.StatusInternalServerError)
@@ -435,7 +517,10 @@ func (h *handler) handleBind(w http.ResponseWriter, r *http.Request) {
435517
},
436518
Spec: kubebindv1alpha2.APIServiceExportRequestSpec{
437519
Resources: []kubebindv1alpha2.APIServiceExportRequestResource{
438-
{GroupResource: kubebindv1alpha2.GroupResource{Group: group, Resource: resource}},
520+
{
521+
GroupResource: kubebindv1alpha2.GroupResource{Group: group, Resource: resource},
522+
Versions: []string{version},
523+
},
439524
},
440525
},
441526
}
@@ -495,3 +580,28 @@ func mustRead(f func(name string) ([]byte, error), name string) string {
495580
}
496581
return string(bs)
497582
}
583+
584+
func (h *handler) getBackendDynamicResource(ctx context.Context, cluster string) (*unstructured.UnstructuredList, error) {
585+
labelSelector := labels.Set{
586+
resources.ExportedCRDsLabel: "true",
587+
}
588+
589+
parts := strings.SplitN(h.schemaSource, ".", 3)
590+
if len(parts) != 3 {
591+
return nil, fmt.Errorf("invalid schema source: %q", h.schemaSource)
592+
}
593+
594+
gvk := schema.GroupVersionKind{
595+
Kind: parts[0],
596+
Version: parts[1],
597+
Group: parts[2],
598+
}
599+
apiResourceSchemas, err := h.kubeManager.ListDynamicResources(ctx, cluster, gvk, labelSelector.AsSelector())
600+
if err != nil {
601+
return nil, fmt.Errorf("failed to list crds: %w", err)
602+
}
603+
sort.SliceStable(apiResourceSchemas.Items, func(i, j int) bool {
604+
return apiResourceSchemas.Items[i].GetName() < apiResourceSchemas.Items[j].GetName()
605+
})
606+
return apiResourceSchemas, nil
607+
}

backend/kubernetes/manager.go

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ package kubernetes
1919
import (
2020
"context"
2121
"fmt"
22+
"strings"
2223

2324
corev1 "k8s.io/api/core/v1"
2425
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2526
"k8s.io/apimachinery/pkg/api/errors"
27+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2628
"k8s.io/apimachinery/pkg/labels"
29+
"k8s.io/apimachinery/pkg/runtime"
30+
"k8s.io/apimachinery/pkg/runtime/schema"
2731
"k8s.io/apimachinery/pkg/types"
2832
"k8s.io/klog/v2"
2933
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,6 +40,7 @@ import (
3640
type Manager struct {
3741
namespacePrefix string
3842
providerPrettyName string
43+
scope kubebindv1alpha2.InformerScope
3944

4045
externalAddressGenerator kuberesources.ExternalAddreesGeneratorFunc
4146
externalCA []byte
@@ -48,13 +53,15 @@ func NewKubernetesManager(
4853
ctx context.Context,
4954
namespacePrefix, providerPrettyName string,
5055
externalAddressGenerator kuberesources.ExternalAddreesGeneratorFunc,
56+
scope kubebindv1alpha2.InformerScope,
5157
externalCA []byte,
5258
externalTLSServerName string,
5359
manager mcmanager.Manager,
5460
) (*Manager, error) {
5561
m := &Manager{
5662
namespacePrefix: namespacePrefix,
5763
providerPrettyName: providerPrettyName,
64+
scope: scope,
5865

5966
externalAddressGenerator: externalAddressGenerator,
6067
externalCA: externalCA,
@@ -71,8 +78,8 @@ func NewKubernetesManager(
7178
return m, nil
7279
}
7380

74-
func (m *Manager) HandleResources(ctx context.Context, identity, cluster, resource, group string) ([]byte, error) {
75-
logger := klog.FromContext(ctx).WithValues("identity", identity, "resource", resource, "group", group)
81+
func (m *Manager) HandleResources(ctx context.Context, identity, cluster string) ([]byte, error) {
82+
logger := klog.FromContext(ctx).WithValues("identity", identity)
7683
ctx = klog.NewContext(ctx, logger)
7784

7885
cl, err := m.manager.GetCluster(ctx, cluster)
@@ -173,3 +180,55 @@ func (m *Manager) ListCustomResourceDefinitions(ctx context.Context, cluster str
173180

174181
return &crds, nil
175182
}
183+
184+
func (m *Manager) ListDynamicResources(ctx context.Context, cluster string, gvk schema.GroupVersionKind, selector labels.Selector) (*unstructured.UnstructuredList, error) {
185+
cl, err := m.manager.GetCluster(ctx, cluster)
186+
if err != nil {
187+
return nil, err
188+
}
189+
c := cl.GetClient()
190+
191+
// Ensure we have the List kind
192+
listGVK := gvk
193+
if !strings.HasSuffix(listGVK.Kind, "List") {
194+
listGVK.Kind = listGVK.Kind + "List"
195+
}
196+
197+
list := &unstructured.UnstructuredList{}
198+
list.SetGroupVersionKind(listGVK)
199+
200+
listOpts := []client.ListOption{}
201+
if selector != nil {
202+
listOpts = append(listOpts, client.MatchingLabelsSelector{Selector: selector})
203+
}
204+
205+
if err := c.List(ctx, list, listOpts...); err != nil {
206+
return nil, err
207+
}
208+
209+
return list, nil
210+
}
211+
212+
func (m *Manager) CreateAPIResourceSchema(ctx context.Context, cluster string, name string, u *unstructured.Unstructured) error {
213+
cl, err := m.manager.GetCluster(ctx, cluster)
214+
if err != nil {
215+
return err
216+
}
217+
c := cl.GetClient()
218+
219+
apiResourceSchema := &kubebindv1alpha2.APIResourceSchema{}
220+
err = runtime.DefaultUnstructuredConverter.FromUnstructured(u.UnstructuredContent(), apiResourceSchema)
221+
if err != nil {
222+
return err
223+
}
224+
225+
apiResourceSchema.ResourceVersion = ""
226+
apiResourceSchema.Name = name
227+
apiResourceSchema.Spec.InformerScope = m.scope
228+
229+
if err := c.Create(ctx, apiResourceSchema); err != nil {
230+
return err
231+
}
232+
233+
return nil
234+
}

0 commit comments

Comments
 (0)