276 lines
6.7 KiB
Go
276 lines
6.7 KiB
Go
package k8s
|
|
|
|
import (
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
"k8s.io/cli-runtime/pkg/genericclioptions"
|
|
"k8s.io/client-go/dynamic"
|
|
"k8s.io/client-go/kubernetes"
|
|
"k8s.io/client-go/rest"
|
|
restclient "k8s.io/client-go/rest"
|
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
|
clientcmd "k8s.io/client-go/tools/clientcmd"
|
|
"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 = &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)
|
|
getClusterName() string
|
|
hasMetricsServer() bool
|
|
}
|
|
|
|
apiServer struct {
|
|
flags *genericclioptions.ConfigFlags
|
|
client kubernetes.Interface
|
|
dClient dynamic.Interface
|
|
csClient *clientset.Clientset
|
|
nsClient dynamic.NamespaceableResourceInterface
|
|
heapsterClient *metricsutil.HeapsterMetricsClient
|
|
mxsClient *versioned.Clientset
|
|
clusterName string
|
|
useMetricServer bool
|
|
}
|
|
)
|
|
|
|
// InitConnection initialize connection from command line args.
|
|
func InitConnection(flags *genericclioptions.ConfigFlags) {
|
|
conn = &apiServer{flags: flags}
|
|
}
|
|
|
|
func (a *apiServer) getClusterName() string {
|
|
a.checkCurrentConfig()
|
|
return a.clusterName
|
|
}
|
|
|
|
func (a *apiServer) hasMetricsServer() bool {
|
|
return a.useMetricServer
|
|
}
|
|
|
|
// ActiveClusterOrDie Fetch the current cluster based on current context.
|
|
func ActiveClusterOrDie() string {
|
|
cfg := conn.apiConfigOrDie()
|
|
return cfg.Contexts[cfg.CurrentContext].Cluster
|
|
}
|
|
|
|
// AllClustersOrDie fetch all available clusters from config.
|
|
func AllClustersOrDie() []string {
|
|
cfg := conn.apiConfigOrDie()
|
|
|
|
cc := make([]string, 0, len(cfg.Clusters))
|
|
for k := range cfg.Clusters {
|
|
cc = append(cc, k)
|
|
}
|
|
return cc
|
|
}
|
|
|
|
// AllNamespacesOrDie fetch all available namespaces on current cluster.
|
|
func AllNamespacesOrDie() []string {
|
|
nn, err := NewNamespace().List("")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
ss := make([]string, len(nn))
|
|
for i, n := range nn {
|
|
ss[i] = n.(v1.Namespace).Name
|
|
}
|
|
return ss
|
|
}
|
|
|
|
// 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.restConfigOrDie()); err != nil {
|
|
panic(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 {
|
|
panic(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.restConfigOrDie()); err != nil {
|
|
panic(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
|
|
}
|
|
|
|
var err error
|
|
a.mxsClient, err = versioned.NewForConfig(a.restConfigOrDie())
|
|
return a.mxsClient, err
|
|
}
|
|
|
|
// ConfigOrDie check kubernetes cluster config.
|
|
// Dies if no config is found or incorrect.
|
|
func ConfigOrDie() {
|
|
cfg := conn.apiConfigOrDie()
|
|
if clientcmdapi.IsConfigEmpty(&cfg) {
|
|
panic("K8s config file load failed. Please check your .kube/config or $KUBECONFIG env")
|
|
}
|
|
}
|
|
|
|
func (a *apiServer) configAccess() clientcmd.ConfigAccess {
|
|
return a.flags.ToRawKubeConfigLoader().ConfigAccess()
|
|
}
|
|
|
|
func (a *apiServer) apiConfigOrDie() clientcmdapi.Config {
|
|
c, err := a.flags.ToRawKubeConfigLoader().RawConfig()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return c
|
|
}
|
|
|
|
func (a *apiServer) restConfigOrDie() *restclient.Config {
|
|
cfg, err := a.flags.ToRESTConfig()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return cfg
|
|
}
|
|
|
|
func (a *apiServer) checkCurrentConfig() {
|
|
cfg := a.apiConfigOrDie()
|
|
currentCluster := cfg.Contexts[cfg.CurrentContext].Cluster
|
|
|
|
if len(a.clusterName) == 0 {
|
|
a.clusterName = currentCluster
|
|
a.useMetricServer = a.supportsMxServer()
|
|
return
|
|
}
|
|
|
|
if a.clusterName != currentCluster {
|
|
a.reset()
|
|
a.clusterName = currentCluster
|
|
a.useMetricServer = a.supportsMxServer()
|
|
}
|
|
}
|
|
|
|
func (a *apiServer) reset() {
|
|
a.client, a.dClient, a.nsClient = nil, nil, nil
|
|
a.heapsterClient, a.mxsClient = nil, nil
|
|
a.clusterName = ""
|
|
}
|
|
|
|
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
|
|
}
|