Created
December 15, 2017 20:53
-
-
Save droot/adb815effabc484b36d13f9573713859 to your computer and use it in GitHub Desktop.
kube-get
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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