package client import ( "context" "fmt" "strings" "time" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/discovery" "k8s.io/client-go/rest" ) var ( defaultPollInterval = 500 * time.Millisecond defaultAvailabilityTimeout = 30 * time.Second ) type DiscoveryClient interface { discovery.DiscoveryInterface GetResourceForKind(gvk schema.GroupVersionKind) (schema.GroupVersionResource, error) GetKindForResource(gvr schema.GroupVersionResource) (schema.GroupVersionKind, error) GetPreferredVesion(gr schema.GroupResource) (schema.GroupVersionResource, schema.GroupVersionKind, error) GetPreferredVersionForKind(gk schema.GroupKind) (schema.GroupVersionResource, schema.GroupVersionKind, error) WaitForAvailability(ctx context.Context, gv schema.GroupVersion) error } type DiscoveryClientImpl struct { restConfig *rest.Config discovery.DiscoveryInterface } func NewDiscoveryClient(restConfig *rest.Config) (*DiscoveryClientImpl, error) { discoveryClient, err := discovery.NewDiscoveryClientForConfig(restConfig) if err != nil { return nil, err } return &DiscoveryClientImpl{ restConfig: restConfig, DiscoveryInterface: discoveryClient, }, nil } func (d *DiscoveryClientImpl) GetResourceForKind(gvk schema.GroupVersionKind) (schema.GroupVersionResource, error) { resourceList, err := d.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) if err != nil { return schema.GroupVersionResource{}, err } for _, resource := range resourceList.APIResources { if resource.Kind == gvk.Kind { return schema.GroupVersionResource{ Group: gvk.Group, Version: gvk.Version, Resource: resource.Name, }, nil } } return schema.GroupVersionResource{}, fmt.Errorf("resource not found for %s", gvk.String()) } func (d *DiscoveryClientImpl) GetKindForResource(gvr schema.GroupVersionResource) (schema.GroupVersionKind, error) { resourceList, err := d.ServerResourcesForGroupVersion(gvr.GroupVersion().String()) if err != nil { return schema.GroupVersionKind{}, err } for _, resource := range resourceList.APIResources { if resource.Name == gvr.Resource { return schema.GroupVersionKind{ Group: gvr.Group, Version: gvr.Version, Kind: resource.Kind, }, nil } } return schema.GroupVersionKind{}, fmt.Errorf("kind not found for %s", gvr.String()) } func (d *DiscoveryClientImpl) GetPreferredVesion(gr schema.GroupResource) (schema.GroupVersionResource, schema.GroupVersionKind, error) { apiList, err := d.ServerPreferredResources() if err != nil { return schema.GroupVersionResource{}, schema.GroupVersionKind{}, fmt.Errorf("getting server's preferred resources: %w", err) } for _, apis := range apiList { if !strings.HasPrefix(apis.GroupVersion, gr.Group) { continue } gv := strings.Split(apis.GroupVersion, "/") for _, resource := range apis.APIResources { if resource.Name == gr.Resource { return schema.GroupVersionResource{ Group: gv[0], Version: gv[1], Resource: resource.Name, }, schema.GroupVersionKind{ Group: gv[0], Version: gv[1], Kind: resource.Kind, }, nil } } } return schema.GroupVersionResource{}, schema.GroupVersionKind{}, fmt.Errorf("preferred version not found for %s", gr.String()) } func (d *DiscoveryClientImpl) GetPreferredVersionForKind(gk schema.GroupKind) (schema.GroupVersionResource, schema.GroupVersionKind, error) { apiList, err := d.ServerPreferredResources() if err != nil { return schema.GroupVersionResource{}, schema.GroupVersionKind{}, err } for _, apis := range apiList { // Check if this API group matches our target group if !strings.HasPrefix(apis.GroupVersion, gk.Group) { continue } // Parse the group/version var group, version string if strings.Contains(apis.GroupVersion, "/") { parts := strings.Split(apis.GroupVersion, "/") group = parts[0] version = parts[1] } else { // Core API group (e.g., "v1") group = "" version = apis.GroupVersion } // Look for our target kind in this API group version for _, resource := range apis.APIResources { if resource.Kind == gk.Kind { return schema.GroupVersionResource{ Group: group, Version: version, Resource: resource.Name, }, schema.GroupVersionKind{ Group: group, Version: version, Kind: resource.Kind, }, nil } } } return schema.GroupVersionResource{}, schema.GroupVersionKind{}, fmt.Errorf("preferred version not found for kind %s in group %s", gk.Kind, gk.Group) } func (d *DiscoveryClientImpl) WaitForAvailability(ctx context.Context, gv schema.GroupVersion) error { return wait.PollUntilContextTimeout(ctx, defaultPollInterval, defaultAvailabilityTimeout, true, func(ctx context.Context) (bool, error) { _, err := d.ServerResourcesForGroupVersion(gv.String()) if err != nil { return false, nil } return true, nil }) }