package k8s import ( "io/ioutil" log "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" clientcmdapi "k8s.io/client-go/tools/clientcmd/api" "k8s.io/kubernetes/pkg/kubectl/metricsutil" "k8s.io/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metricsapi "k8s.io/metrics/pkg/apis/metrics" versioned "k8s.io/metrics/pkg/client/clientset/versioned" ) // NA Not available const NA = "n/a" var ( conn connection supportedMetricsAPIVersions = []string{"v1beta1"} ) func init() { conn = &apiServer{} } type ( ApiGroup struct { Resource string Group, Kind, Version string Plural, Singular string Aliases []string } // Collection of empty interfaces. Collection []interface{} // Res K8s api server calls Res interface { Get(ns string, name string) (interface{}, error) List(ns string) (Collection, error) Delete(ns string, name string) error } connection interface { configOrDie() *restclient.Config dialConfigOrDie() *clientcmdapi.Config dialOrDie() kubernetes.Interface dynDialOrDie() dynamic.Interface nsDialOrDie() dynamic.NamespaceableResourceInterface mxsDial() (*versioned.Clientset, error) heapsterDial() (*metricsutil.HeapsterMetricsClient, error) getClusterName() string setClusterName(n string) hasMetricsServer() bool } apiServer struct { client kubernetes.Interface dClient dynamic.Interface csClient *clientset.Clientset nsClient dynamic.NamespaceableResourceInterface heapsterClient *metricsutil.HeapsterMetricsClient mxsClient *versioned.Clientset cluster string useMetricServer bool } ) func (a *apiServer) getClusterName() string { return a.cluster } func (a *apiServer) setClusterName(n string) { a.cluster = n } func (a *apiServer) hasMetricsServer() bool { return a.useMetricServer } func (a *apiServer) dialConfigOrDie() *clientcmdapi.Config { c, err := clientcmd.NewDefaultPathOptions().GetStartingConfig() if err != nil { log.Fatal(err) } return c } // DialOrDie returns a handle to api server or die. func (a *apiServer) dialOrDie() kubernetes.Interface { a.checkCurrentConfig() if a.client != nil { return a.client } var err error if a.client, err = kubernetes.NewForConfig(a.configOrDie()); err != nil { log.Fatal(err) } return a.client } func (a *apiServer) csDialOrDie() *clientset.Clientset { a.checkCurrentConfig() if a.csClient != nil { return a.csClient } var cfg *rest.Config // cfg := clientcmd.NewNonInteractiveClientConfig(config, contextName, overrides, configAccess) var err error if a.csClient, err = clientset.NewForConfig(cfg); err != nil { log.Fatal(err) } return a.csClient } // DynDial returns a handle to the api server. func (a *apiServer) dynDialOrDie() dynamic.Interface { a.checkCurrentConfig() if a.dClient != nil { return a.dClient } var err error if a.dClient, err = dynamic.NewForConfig(a.configOrDie()); err != nil { log.Fatal(err) } return a.dClient } func (a *apiServer) nsDialOrDie() dynamic.NamespaceableResourceInterface { a.checkCurrentConfig() if a.nsClient != nil { return a.nsClient } a.nsClient = a.dynDialOrDie().Resource(schema.GroupVersionResource{ Group: "apiextensions.k8s.io", Version: "v1beta1", Resource: "customresourcedefinitions", }) return a.nsClient } func (a *apiServer) heapsterDial() (*metricsutil.HeapsterMetricsClient, error) { a.checkCurrentConfig() if a.heapsterClient != nil { return a.heapsterClient, nil } a.heapsterClient = metricsutil.NewHeapsterMetricsClient( a.dialOrDie().CoreV1(), metricsutil.DefaultHeapsterNamespace, metricsutil.DefaultHeapsterScheme, metricsutil.DefaultHeapsterService, metricsutil.DefaultHeapsterPort, ) return a.heapsterClient, nil } func (a *apiServer) mxsDial() (*versioned.Clientset, error) { a.checkCurrentConfig() if a.mxsClient != nil { return a.mxsClient, nil } opts := clientcmd.NewDefaultPathOptions() dat, err := ioutil.ReadFile(opts.GetDefaultFilename()) if err != nil { return nil, err } rc, err := clientcmd.RESTConfigFromKubeConfig(dat) if err != nil { return nil, err } a.mxsClient, err = versioned.NewForConfig(rc) return a.mxsClient, err } func (*apiServer) configOrDie() *restclient.Config { opts := clientcmd.NewDefaultPathOptions() cfg, err := clientcmd.BuildConfigFromFlags("", opts.GetDefaultFilename()) if err != nil { log.Fatal(err) } return cfg } func (a *apiServer) checkCurrentConfig() { cfg, err := clientcmd.NewDefaultPathOptions().GetStartingConfig() if err != nil { log.Fatal(err) } if len(a.getClusterName()) == 0 { a.setClusterName(cfg.CurrentContext) a.useMetricServer = a.supportsMxServer() return } if a.getClusterName() != cfg.CurrentContext { a.reset() a.setClusterName(cfg.CurrentContext) a.useMetricServer = a.supportsMxServer() } } func (a *apiServer) reset() { a.client, a.dClient, a.nsClient = nil, nil, nil a.heapsterClient, a.mxsClient = nil, nil a.setClusterName("") } func (a *apiServer) supportsMxServer() bool { apiGroups, err := a.dialOrDie().Discovery().ServerGroups() if err != nil { return false } for _, discoveredAPIGroup := range apiGroups.Groups { if discoveredAPIGroup.Name != metricsapi.GroupName { continue } for _, version := range discoveredAPIGroup.Versions { for _, supportedVersion := range supportedMetricsAPIVersions { if version.Version == supportedVersion { return true } } } } return false }