CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/golang-k8s-io--client-go

Official Go client library for Kubernetes API - typed clients, controllers, and cluster interaction tools

Overview
Eval results
Files

discovery.mddocs/reference/

Discovery - API Discovery and Capabilities

Back to Index

The discovery client allows you to discover what APIs, resources, and versions are available in a Kubernetes cluster at runtime.

Package Information

  • Package: k8s.io/client-go/discovery
  • Cached Discovery: k8s.io/client-go/discovery/cached
  • REST Mapper: k8s.io/client-go/restmapper

Core Imports

import (
    "k8s.io/client-go/discovery"
    "k8s.io/apimachinery/pkg/runtime/schema"
    metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Creating a Discovery Client

import (
    "k8s.io/client-go/discovery"
    "k8s.io/client-go/kubernetes"
)

// From config
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)

// From clientset
clientset, _ := kubernetes.NewForConfig(config)
discoveryClient := clientset.Discovery()

DiscoveryInterface

type DiscoveryInterface interface {
    RESTClient() rest.Interface
    ServerGroupsInterface
    ServerResourcesInterface
    ServerVersionInterface
    OpenAPISchemaInterface
    OpenAPIV3SchemaInterface
}

type ServerGroupsInterface interface {
    ServerGroups() (*metav1.APIGroupList, error)
}

type ServerResourcesInterface interface {
    ServerResourcesForGroupVersion(groupVersion string) (*metav1.APIResourceList, error)
    ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)
    ServerPreferredResources() ([]*metav1.APIResourceList, error)
    ServerPreferredNamespacedResources() ([]*metav1.APIResourceList, error)
}

type ServerVersionInterface interface {
    ServerVersion() (*version.Info, error)
}

Discovering API Groups

// Get all API groups
groups, err := discoveryClient.ServerGroups()
if err != nil {
    panic(err)
}

for _, group := range groups.Groups {
    fmt.Printf("Group: %s\n", group.Name)
    fmt.Printf("  Preferred Version: %s\n", group.PreferredVersion.Version)
    for _, version := range group.Versions {
        fmt.Printf("  Available Version: %s\n", version.Version)
    }
}

// Example output:
// Group: apps
//   Preferred Version: v1
//   Available Version: v1
//   Available Version: v1beta2
//   Available Version: v1beta1

Discovering Resources

// Get resources for specific API group version
resourceList, err := discoveryClient.ServerResourcesForGroupVersion("apps/v1")
if err != nil {
    panic(err)
}

fmt.Printf("Group Version: %s\n", resourceList.GroupVersion)
for _, resource := range resourceList.APIResources {
    fmt.Printf("  Resource: %s\n", resource.Name)
    fmt.Printf("    Namespaced: %v\n", resource.Namespaced)
    fmt.Printf("    Kind: %s\n", resource.Kind)
    fmt.Printf("    Verbs: %v\n", resource.Verbs)
    fmt.Printf("    ShortNames: %v\n", resource.ShortNames)
}

// Example output:
// Group Version: apps/v1
//   Resource: deployments
//     Namespaced: true
//     Kind: Deployment
//     Verbs: [create delete deletecollection get list patch update watch]
//     ShortNames: [deploy]

Get All Resources

// Get all API groups and resources
groups, resources, err := discoveryClient.ServerGroupsAndResources()
if err != nil {
    // Note: This may return partial results with error
    // if some API groups are unavailable
}

for _, resourceList := range resources {
    fmt.Printf("API Group Version: %s\n", resourceList.GroupVersion)
    for _, resource := range resourceList.APIResources {
        fmt.Printf("  %s (%s)\n", resource.Name, resource.Kind)
    }
}

Preferred Resources

// Get preferred version resources only
resources, err := discoveryClient.ServerPreferredResources()
if err != nil {
    panic(err)
}

// Get preferred namespaced resources only
resources, err := discoveryClient.ServerPreferredNamespacedResources()

Server Version

// Get Kubernetes server version
versionInfo, err := discoveryClient.ServerVersion()
if err != nil {
    panic(err)
}

fmt.Printf("Major: %s\n", versionInfo.Major)
fmt.Printf("Minor: %s\n", versionInfo.Minor)
fmt.Printf("GitVersion: %s\n", versionInfo.GitVersion)
fmt.Printf("Platform: %s\n", versionInfo.Platform)

// Example output:
// Major: 1
// Minor: 27
// GitVersion: v1.27.3
// Platform: linux/amd64

Cached Discovery

For better performance, use cached discovery:

import (
    "k8s.io/client-go/discovery/cached/memory"
    "k8s.io/client-go/restmapper"
)

// Create memory-cached discovery client
cachedDiscovery := memory.NewMemCacheClient(discoveryClient)

// Use cached client for discovery operations
groups, err := cachedDiscovery.ServerGroups()

// Invalidate cache
cachedDiscovery.Invalidate()

REST Mapper

REST mapper converts between GroupVersionKind (GVK) and GroupVersionResource (GVR):

import "k8s.io/client-go/restmapper"

// Create REST mapper from discovery
groupResources, err := restmapper.GetAPIGroupResources(discoveryClient)
if err != nil {
    panic(err)
}

mapper := restmapper.NewDiscoveryRESTMapper(groupResources)

// Convert GVK to GVR
gvk := schema.GroupVersionKind{
    Group:   "apps",
    Version: "v1",
    Kind:    "Deployment",
}

mapping, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
    panic(err)
}

gvr := mapping.Resource
fmt.Printf("GVR: %s\n", gvr.String()) // apps/v1, Resource=deployments

// Check if resource is namespaced
fmt.Printf("Namespaced: %v\n", mapping.Scope.Name() == meta.RESTScopeNameNamespace)

// Convert partial GVK (find preferred version)
gvk = schema.GroupVersionKind{
    Group: "apps",
    Kind:  "Deployment",
}

mapping, err = mapper.RESTMapping(gvk.GroupKind())
if err != nil {
    panic(err)
}

fmt.Printf("Preferred GVR: %s\n", mapping.Resource.String())

Resource Discovery Helper

// Check if a resource exists
func ResourceExists(discoveryClient discovery.DiscoveryInterface, gvr schema.GroupVersionResource) (bool, error) {
    groupVersion := gvr.GroupVersion().String()
    resourceList, err := discoveryClient.ServerResourcesForGroupVersion(groupVersion)
    if err != nil {
        return false, err
    }

    for _, resource := range resourceList.APIResources {
        if resource.Name == gvr.Resource {
            return true, nil
        }
    }

    return false, nil
}

// Find resource by kind
func FindResourceByKind(discoveryClient discovery.DiscoveryInterface, kind string) (*metav1.APIResource, schema.GroupVersion, error) {
    _, resources, err := discoveryClient.ServerGroupsAndResources()
    if err != nil {
        return nil, schema.GroupVersion{}, err
    }

    for _, resourceList := range resources {
        gv, err := schema.ParseGroupVersion(resourceList.GroupVersion)
        if err != nil {
            continue
        }

        for _, resource := range resourceList.APIResources {
            if resource.Kind == kind {
                return &resource, gv, nil
            }
        }
    }

    return nil, schema.GroupVersion{}, fmt.Errorf("resource not found for kind: %s", kind)
}

Complete Example

package main

import (
    "fmt"

    "k8s.io/client-go/discovery"
    "k8s.io/client-go/tools/clientcmd"
)

func main() {
    // Load config
    config, err := clientcmd.BuildConfigFromFlags("", clientcmd.RecommendedHomeFile)
    if err != nil {
        panic(err)
    }

    // Create discovery client
    discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
    if err != nil {
        panic(err)
    }

    // Get server version
    version, err := discoveryClient.ServerVersion()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Kubernetes version: %s\n\n", version.GitVersion)

    // Get all API groups
    groups, err := discoveryClient.ServerGroups()
    if err != nil {
        panic(err)
    }

    fmt.Println("Available API Groups:")
    for _, group := range groups.Groups {
        fmt.Printf("- %s (preferred: %s)\n", group.Name, group.PreferredVersion.Version)
    }

    fmt.Println("\nCore v1 Resources:")
    coreResources, err := discoveryClient.ServerResourcesForGroupVersion("v1")
    if err != nil {
        panic(err)
    }

    for _, resource := range coreResources.APIResources {
        nsStatus := "cluster-scoped"
        if resource.Namespaced {
            nsStatus = "namespaced"
        }
        fmt.Printf("- %s (%s, %s)\n", resource.Name, resource.Kind, nsStatus)
    }
}

Use Cases

  1. Dynamic Tool Building: Build kubectl-like tools that discover available resources
  2. CRD Detection: Check if Custom Resources are installed before accessing them
  3. Version Compatibility: Verify API availability before using specific features
  4. Resource Listing: Build UI tools that list all available resource types
  5. REST Mapping: Convert between GVK and GVR for dynamic clients
  6. API Exploration: Explore unfamiliar clusters programmatically

Best Practices

  1. Use Caching: Cache discovery results to avoid repeated API calls
  2. Handle Partial Failures: ServerGroupsAndResources may return partial results
  3. Check Existence: Verify resource existence before accessing, especially for CRDs
  4. Use REST Mapper: Use REST mapper for GVK/GVR conversions
  5. Invalidate Cache: Invalidate discovery cache when CRDs are installed/removed
  6. Handle Errors: Discovery may fail if some API servers are unavailable

Related Documentation

Back to Index

Install with Tessl CLI

npx tessl i tessl/golang-k8s-io--client-go@0.35.0

docs

index.md

tile.json