Skip to content

Commit

Permalink
Use API discovery to determine the preferred API versions for Gateway…
Browse files Browse the repository at this point in the history
… API types.

Clusters may not always have the v1 CRDs installed. This means gwctl will have
to use whatever version is available in the cluster. Hence, we will use API
Discovery to determine the "preferred" API version as per the kube-apiserver and
then use a dynamic client to fetch that specific versioned resource.

Once that specific versioned resource is fetched (e.g. v1beta1.Gateway), it will
be converted to a concrete type used within gwctl (eg. v1.Gateway), which will
usually be a more newer/stable version. For most cases, this conversion should
not result in any losses. If in the future, a need arises for such special
cases, we should be able to extend the approach by configuring some conversion
functions (eg. conversion function for v1beta1.Gateway -> v1.Gateway).
  • Loading branch information
gauravkghildiyal committed Apr 23, 2024
1 parent 2d41da5 commit 6cf42ae
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 63 deletions.
5 changes: 1 addition & 4 deletions gwctl/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ func runDescribe(cmd *cobra.Command, args []string, params *utils.CmdParams) {
ns = metav1.NamespaceAll
}

discoverer := resourcediscovery.Discoverer{
K8sClients: params.K8sClients,
PolicyManager: params.PolicyManager,
}
discoverer := resourcediscovery.NewDiscoverer(params.K8sClients, params.PolicyManager)
policiesPrinter := &printer.PoliciesPrinter{Out: params.Out}
httpRoutesPrinter := &printer.HTTPRoutesPrinter{Out: params.Out}
gwPrinter := &printer.GatewaysPrinter{Out: params.Out}
Expand Down
5 changes: 1 addition & 4 deletions gwctl/cmd/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ func runGet(cmd *cobra.Command, args []string, params *utils.CmdParams) {
ns = ""
}

discoverer := resourcediscovery.Discoverer{
K8sClients: params.K8sClients,
PolicyManager: params.PolicyManager,
}
discoverer := resourcediscovery.NewDiscoverer(params.K8sClients, params.PolicyManager)
realClock := clock.RealClock{}
nsPrinter := &printer.NamespacesPrinter{Out: params.Out, Clock: realClock}
gwPrinter := &printer.GatewaysPrinter{Out: params.Out, Clock: realClock}
Expand Down
46 changes: 45 additions & 1 deletion gwctl/pkg/common/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
fakedynamicclient "k8s.io/client-go/dynamic/fake"
Expand Down Expand Up @@ -94,9 +95,52 @@ func MustClientsForTest(t *testing.T, initRuntimeObjects ...runtime.Object) *K8s
}

fakeClient := fakeclient.NewClientBuilder().WithScheme(scheme).WithRuntimeObjects(initRuntimeObjects...).Build()
fakeDC := fakedynamicclient.NewSimpleDynamicClient(scheme, initRuntimeObjects...)
fakeDiscoveryClient := fakeclientset.NewSimpleClientset().Discovery()

// Setup a fake DynamicClient, which requires some special handling.
//
// When objects are injected using `NewSimpleDynamicClient` or
// `NewSimpleDynamicClientWithCustomListKinds` to the fake resource tracker,
// internally it will use the `meta.UnsafeGuessKindToResource()` function.
// This incorrectly guesses the GVR of Gateway with Resource being guessed as
// "gatewaies" (missing 'y'). In order to get around this, we will have to
// create Gateway objects separately.
//
// Also, becomes of this incorrect guessing, we need to register the
// GatewayList type separately as well.
gatewayv1GVR := schema.GroupVersionResource{
Group: gatewayv1.GroupVersion.Group,
Version: gatewayv1.GroupVersion.Version,
Resource: "gateways",
}
gvrToListKind := map[schema.GroupVersionResource]string{
gatewayv1GVR: "GatewayList",
}
for _, obj := range initRuntimeObjects {
if crd, ok := obj.(*apiextensionsv1.CustomResourceDefinition); ok {
gvr := schema.GroupVersionResource{
Group: crd.Spec.Group,
Version: crd.Spec.Versions[0].Name,
Resource: crd.Spec.Names.Plural, // CRD Kinds directly map to the Resource.
}
gvrToListKind[gvr] = crd.Spec.Names.Kind + "List"
}
}
fakeDC := fakedynamicclient.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind)
for _, obj := range initRuntimeObjects {
var err error
if gateway, ok := obj.(*gatewayv1.Gateway); ok {
// Register Gateway with correct GVR (since the guessed GVR is incorrect).
err = fakeDC.Tracker().Create(gatewayv1GVR, gateway, gateway.Namespace)
} else {
// Register non-Gateway resources automatically without GVR.
err = fakeDC.Tracker().Add(obj)
}
if err != nil {
t.Fatalf("Failed to add object to fake DynamicClient: %v", err)
}
}

return &K8sClients{
Client: fakeClient,
DC: fakeDC,
Expand Down
Loading

0 comments on commit 6cf42ae

Please sign in to comment.