k9s/internal/k8s/api.go

190 lines
4.5 KiB
Go

package k8s
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
clientcmd "k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/metricsutil"
metricsapi "k8s.io/metrics/pkg/apis/metrics"
versioned "k8s.io/metrics/pkg/client/clientset/versioned"
)
// NA Not available
const NA = "n/a"
var (
conn = &apiServer{}
supportedMetricsAPIVersions = []string{"v1beta1"}
)
type (
// APIGroup represents a K8s resource descriptor.
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 represents a k8s api server connection.
connection interface {
configAccess() clientcmd.ConfigAccess
restConfigOrDie() *restclient.Config
apiConfigOrDie() clientcmdapi.Config
dialOrDie() kubernetes.Interface
dynDialOrDie() dynamic.Interface
nsDialOrDie() dynamic.NamespaceableResourceInterface
mxsDial() (*versioned.Clientset, error)
heapsterDial() (*metricsutil.HeapsterMetricsClient, error)
hasMetricsServer() bool
}
apiServer struct {
config *Config
client kubernetes.Interface
dClient dynamic.Interface
nsClient dynamic.NamespaceableResourceInterface
heapsterClient *metricsutil.HeapsterMetricsClient
mxsClient *versioned.Clientset
useMetricServer bool
}
)
// InitConnectionOrDie initialize connection from command line args.
// Checks for connectivity with the api server.
func InitConnectionOrDie(config *Config) {
conn = &apiServer{config: config}
conn.useMetricServer = conn.supportsMxServer()
}
func (a *apiServer) hasMetricsServer() bool {
return a.useMetricServer
}
// DialOrDie returns a handle to api server or die.
func (a *apiServer) dialOrDie() kubernetes.Interface {
if a.client != nil {
return a.client
}
var err error
if a.client, err = kubernetes.NewForConfig(a.restConfigOrDie()); err != nil {
panic(err)
}
return a.client
}
// DynDial returns a handle to the api server.
func (a *apiServer) dynDialOrDie() dynamic.Interface {
if a.dClient != nil {
return a.dClient
}
var err error
if a.dClient, err = dynamic.NewForConfig(a.restConfigOrDie()); err != nil {
panic(err)
}
return a.dClient
}
func (a *apiServer) nsDialOrDie() dynamic.NamespaceableResourceInterface {
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) {
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) {
if a.mxsClient != nil {
return a.mxsClient, nil
}
var err error
a.mxsClient, err = versioned.NewForConfig(a.restConfigOrDie())
return a.mxsClient, err
}
func (a *apiServer) restConfigOrDie() *restclient.Config {
cfg, err := a.config.RESTConfig()
if err != nil {
panic(err)
}
return cfg
}
func (a *apiServer) switchContextOrDie(ctx string) {
currentCtx, err := a.config.CurrentContextName()
if err != nil {
panic(err)
}
if currentCtx != ctx {
a.reset()
if err := a.config.SwitchContext(ctx); err != nil {
panic(err)
}
a.useMetricServer = a.supportsMxServer()
}
}
func (a *apiServer) reset() {
a.client, a.dClient, a.nsClient = nil, nil, nil
a.heapsterClient, a.mxsClient = nil, nil
}
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
}