Skip to content

fix: panic in util.GVRFromType for structured types #2553

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 29, 2025
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion internal/discovery/discovery.go
Original file line number Diff line number Diff line change
@@ -216,7 +216,16 @@ func (r *CRDiscoverer) PollForCacheUpdates(
// Update the list of enabled custom resources.
var enabledCustomResources []string
for _, factory := range customFactories {
gvrString := util.GVRFromType(factory.Name(), factory.ExpectedType()).String()
gvr, err := util.GVRFromType(factory.Name(), factory.ExpectedType())
if err != nil {
klog.ErrorS(err, "failed to update custom resource stores")
}
var gvrString string
if gvr != nil {
gvrString = gvr.String()
} else {
gvrString = factory.Name()
}
enabledCustomResources = append(enabledCustomResources, gvrString)
}
// Create clients for discovered factories.
10 changes: 8 additions & 2 deletions internal/store/builder.go
Original file line number Diff line number Diff line change
@@ -197,7 +197,10 @@ func (b *Builder) DefaultGenerateCustomResourceStoresFunc() ksmtypes.BuildCustom
func (b *Builder) WithCustomResourceStoreFactories(fs ...customresource.RegistryFactory) {
for i := range fs {
f := fs[i]
gvr := util.GVRFromType(f.Name(), f.ExpectedType())
gvr, err := util.GVRFromType(f.Name(), f.ExpectedType())
if err != nil {
klog.ErrorS(err, "Failed to get GVR from type", "resourceName", f.Name(), "expectedType", f.ExpectedType())
}
var gvrString string
if gvr != nil {
gvrString = gvr.String()
@@ -553,7 +556,10 @@ func (b *Builder) buildCustomResourceStores(resourceName string,

familyHeaders := generator.ExtractMetricFamilyHeaders(metricFamilies)

gvr := util.GVRFromType(resourceName, expectedType)
gvr, err := util.GVRFromType(resourceName, expectedType)
if err != nil {
klog.ErrorS(err, "Failed to get GVR from type", "resourceName", resourceName, "expectedType", expectedType)
}
var gvrString string
if gvr != nil {
gvrString = gvr.String()
6 changes: 5 additions & 1 deletion pkg/customresourcestate/config.go
Original file line number Diff line number Diff line change
@@ -204,7 +204,11 @@ func FromConfig(decoder ConfigDecoder, discovererInstance *discovery.CRDiscovere
if err != nil {
return nil, fmt.Errorf("failed to create metrics factory for %s: %w", resource.GroupVersionKind, err)
}
gvrString := util.GVRFromType(factory.Name(), factory.ExpectedType()).String()
gvr, err := util.GVRFromType(factory.Name(), factory.ExpectedType())
if err != nil {
return nil, fmt.Errorf("failed to create GVR for %s: %w", resource.GroupVersionKind, err)
}
gvrString := gvr.String()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need a similar check as 61134a0 (#2553) here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hesitated.

The thing is that the if block that checks if gvrString is nil and adapts if it is that I added in internal/discovery/discovery.go:224 has been copied from internal/store/builder.go:205 and internal/store/builder.go:564 because, in those three cases, if util.GVRFromType(…) returns an error, we only log an error and continue executing the function.
But, in this case and and in pkg/util/utils.go below, if util.GVRFromType(…) returns an error, the function forwards the error and exits early so that this line isn’t executed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rexagod I’ve just fixed this function to make it gracefully handle the cases where utils.GVRFromType(…) returns neither an error nor a valid GVR: c3b1afe

if _, ok := factoriesIndex[gvrString]; ok {
klog.InfoS("reloaded factory", "GVR", gvrString)
}
21 changes: 14 additions & 7 deletions pkg/util/utils.go
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ import (
"runtime"
"strings"

"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
@@ -97,8 +97,11 @@ func CreateCustomResourceClients(apiserver string, kubeconfig string, factories
if err != nil {
return nil, err
}
gvrString := GVRFromType(f.Name(), f.ExpectedType()).String()
customResourceClients[gvrString] = customResourceClient
gvr, err := GVRFromType(f.Name(), f.ExpectedType())
if err != nil {
return nil, err
}
customResourceClients[gvr.String()] = customResourceClient
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fixed in c3b1afe

}
return customResourceClients, nil
}
@@ -137,12 +140,16 @@ func CreateDynamicClient(apiserver string, kubeconfig string) (*dynamic.DynamicC
}

// GVRFromType returns the GroupVersionResource for a given type.
func GVRFromType(resourceName string, expectedType interface{}) *schema.GroupVersionResource {
func GVRFromType(resourceName string, expectedType interface{}) (*schema.GroupVersionResource, error) {
if _, ok := expectedType.(*testUnstructuredMock.Foo); ok {
// testUnstructuredMock.Foo is a mock type for testing
return nil
return nil, nil
}
apiVersion := expectedType.(*unstructured.Unstructured).Object["apiVersion"].(string)
t, err := meta.TypeAccessor(expectedType)
if err != nil {
return nil, fmt.Errorf("Failed to get type accessor for %T: %w", expectedType, err)
}
apiVersion := t.GetAPIVersion()
g, v, found := strings.Cut(apiVersion, "/")
if !found {
g = "core"
@@ -153,7 +160,7 @@ func GVRFromType(resourceName string, expectedType interface{}) *schema.GroupVer
Group: g,
Version: v,
Resource: r,
}
}, nil
}

// GatherAndCount gathers all metrics from the provided Gatherer and counts