Skip to content

Instantly share code, notes, and snippets.

@droot
Created December 15, 2017 20:53
Show Gist options
  • Save droot/adb815effabc484b36d13f9573713859 to your computer and use it in GitHub Desktop.
Save droot/adb815effabc484b36d13f9573713859 to your computer and use it in GitHub Desktop.
kube-get
package main
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/discovery"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/categories"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
)
//
// steps
//
// Get kubeconfig either from a flag or from default config
// Get RestClient from that config
// Get DiscoveryClient
// Explore CategoryExpander
// Setup the Mapper for Internal and Unstructured
// Create the ResourceBuilder
// Performs a Get Command
//
func main() {
fmt.Println(".........Kube Get from scratch...............")
kubeconfig := filepath.Join(homeDir(), ".kube", "config")
// use the current context in kubeconfig
config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
panic(err.Error())
}
fmt.Printf("read kubeconfig successfully. %+v \n", config)
// get discovery client
// clientset := kubernetes.NewForConfigOrDie(config)
fmt.Println("created client set successfully.")
// discovery := clientset.Discovery()
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil {
panic(err.Error())
}
cacheDir := computeDiscoverCacheDir(filepath.Join(homeDir(), ".kube", "cache", "discovery"), config.Host)
cdc := cmdutil.NewCachedDiscoveryClient(discoveryClient, cacheDir, time.Duration(10*time.Minute))
// fmt.Println("created discovery successfully.")
catExpander := CategoryExpander(cdc)
fmt.Printf("created category expander successfully %+v", catExpander)
clientMapperFunc := resource.ClientMapperFunc(ClientForMapping)
unstructuredClientMapperFunc := resource.ClientMapperFunc(UnstructuredClientForMapping)
mapper, typer := Object(cdc)
rb := resource.NewBuilder(
&resource.Mapper{
RESTMapper: mapper,
ObjectTyper: typer,
ClientMapper: clientMapperFunc,
Decoder: decoder(true),
},
&resource.Mapper{
RESTMapper: mapper,
ObjectTyper: typer,
ClientMapper: unstructuredClientMapperFunc,
Decoder: unstructured.UnstructuredJSONScheme,
},
catExpander,
)
fmt.Println("built resource builder successfully.")
}
func CategoryExpander(dc discovery.DiscoveryInterface) categories.CategoryExpander {
legacyExpander := categories.LegacyCategoryExpander
// discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
discoveryClient := dc
// if err == nil {
// fallback is the legacy expander wrapped with discovery based filtering
fallbackExpander, err := categories.NewDiscoveryFilteredExpander(legacyExpander, discoveryClient)
// CheckErr(err)
if err != nil {
return legacyExpander
}
// by default use the expander that discovers based on "categories" field from the API
discoveryCategoryExpander, err := categories.NewDiscoveryCategoryExpander(fallbackExpander, discoveryClient)
if err != nil {
return legacyExpander
}
return discoveryCategoryExpander
}
func homeDir() string {
if h := os.Getenv("HOME"); h != "" {
return h
}
return os.Getenv("USERPROFILE") // windows
}
func ClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
kubeconfig := filepath.Join(homeDir(), ".kube", "config")
// use the current context in kubeconfig
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, err
}
// cfg, err := f.clientAccessFactory.ClientConfig()
// if err != nil {
// return nil, err
// }
// if err := client.SetKubernetesDefaults(cfg); err != nil {
// return nil, err
// }
gvk := mapping.GroupVersionKind
switch gvk.Group {
case api.GroupName:
cfg.APIPath = "/api"
default:
cfg.APIPath = "/apis"
}
gv := gvk.GroupVersion()
cfg.GroupVersion = &gv
return rest.RESTClientFor(cfg)
}
func UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error) {
kubeconfig := filepath.Join(homeDir(), ".kube", "config")
// use the current context in kubeconfig
cfg, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, err
}
// cfg, err := f.clientAccessFactory.BareClientConfig()
// if err != nil {
// return nil, err
// }
// if err := restclient.SetKubernetesDefaults(cfg); err != nil {
// return nil, err
// }
cfg.APIPath = "/apis"
if mapping.GroupVersionKind.Group == api.GroupName {
cfg.APIPath = "/api"
}
gv := mapping.GroupVersionKind.GroupVersion()
cfg.ContentConfig = dynamic.ContentConfig()
cfg.GroupVersion = &gv
return rest.RESTClientFor(cfg)
}
func Object(dc discovery.DiscoveryInterface) (meta.RESTMapper, runtime.ObjectTyper) {
return meta.NewLazyObjectLoader(objectLoader(dc))
}
// objectLoader attempts to perform discovery against the server, and will fall back to
// the built in mapper if necessary. It supports unstructured objects either way, since
// the underlying Scheme supports Unstructured. The mapper will return converters that can
// convert versioned types to unstructured and back.
func objectLoader(dc discovery.DiscoveryInterface) func() (meta.RESTMapper, runtime.ObjectTyper, error) {
return func() (meta.RESTMapper, runtime.ObjectTyper, error) {
// discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
// if err != nil {
// glog.V(3).Infof("Unable to get a discovery client to find server resources, falling back to hardcoded types: %v", err)
// return legacyscheme.Registry.RESTMapper(), legacyscheme.Scheme, nil
// }
//
groupResources, err := discovery.GetAPIGroupResources(dc)
// if err != nil && !dc.Fresh() {
// dc.Invalidate()
// groupResources, err = discovery.GetAPIGroupResources(dc)
// }
if err != nil {
return legacyscheme.Registry.RESTMapper(), legacyscheme.Scheme, nil
}
// allow conversion between typed and unstructured objects
interfaces := meta.InterfacesForUnstructuredConversion(legacyscheme.Registry.InterfacesFor)
mapper := discovery.NewDeferredDiscoveryRESTMapper(dc, meta.VersionInterfacesFunc(interfaces))
// TODO: should this also indicate it recognizes typed objects?
typer := discovery.NewUnstructuredObjectTyper(groupResources, legacyscheme.Scheme)
expander := NewShortcutExpander(mapper, dc)
return expander, typer, err
}
}
// var kubeconfig string
// if home := homeDir(); home != "" {
// } else {
// kubeconfig = getStringFlag("kubeconfig", "")
// }
// c.apiGroup = getStringFlag("api-group", "")
// c.apiVersion = getStringFlag("api-version", "")
// flag.Parse()
// func NewBuilder() *resource.Builder {
// // optionalClientConfig clientcmd.ClientConfig
//
// clientAccessFactory := NewClientAccessFactory(optionalClientConfig)
// objectMappingFactory := NewObjectMappingFactory(clientAccessFactory)
//
// clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping)
// mapper, typer := f.objectMappingFactory.Object()
//
// unstructuredClientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping)
//
// // discoveryClient :=
// var dc discovery.CachedDiscoveryInterface
// categoryExpander := CategoryExpander(dc)
//
// return resource.NewBuilder(
// &resource.Mapper{
// RESTMapper: mapper,
// ObjectTyper: typer,
// ClientMapper: clientMapperFunc,
// Decoder: decoder(true),
// },
// &resource.Mapper{
// RESTMapper: mapper,
// ObjectTyper: typer,
// ClientMapper: unstructuredClientMapperFunc,
// Decoder: unstructured.UnstructuredJSONScheme,
// },
// categoryExpander,
// )
// }
//
func decoder(toInternal bool) runtime.Decoder {
var decoder runtime.Decoder
if toInternal {
decoder = legacyscheme.Codecs.UniversalDecoder()
} else {
decoder = legacyscheme.Codecs.UniversalDeserializer()
}
return decoder
}
//
//
// overlyCautiousIllegalFileCharacters matches characters that *might* not be supported. Windows is really restrictive, so this is really restrictive
var overlyCautiousIllegalFileCharacters = regexp.MustCompile(`[^(\w/\.)]`)
// computeDiscoverCacheDir takes the parentDir and the host and comes up with a "usually non-colliding" name.
func computeDiscoverCacheDir(parentDir, host string) string {
// strip the optional scheme from host if its there:
schemelessHost := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1)
// now do a simple collapse of non-AZ09 characters. Collisions are possible but unlikely. Even if we do collide the problem is short lived
safeHost := overlyCautiousIllegalFileCharacters.ReplaceAllString(schemelessHost, "_")
return filepath.Join(parentDir, safeHost)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment