refactor + added node view and sorters

mine
derailed 2019-03-24 16:05:23 -06:00
parent 7542304adc
commit 6c3c9159de
38 changed files with 980 additions and 837 deletions

View File

@ -3,14 +3,14 @@ package k8s
import ( import (
"strings" "strings"
"github.com/rs/zerolog/log" "github.com/rs/zerolog"
authorizationv1 "k8s.io/api/authorization/v1" authorizationv1 "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
) )
// CanIAccess checks if user has access to a certain resource. // CanIAccess checks if user has access to a certain resource.
func CanIAccess(ns, verb, name, resURL string) bool { func CanIAccess(cfg *Config, log zerolog.Logger, ns, verb, name, resURL string) bool {
_, gr := schema.ParseResourceArg(strings.ToLower(resURL)) _, gr := schema.ParseResourceArg(strings.ToLower(resURL))
sar := &authorizationv1.SelfSubjectAccessReview{ sar := &authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{ Spec: authorizationv1.SelfSubjectAccessReviewSpec{
@ -25,15 +25,21 @@ func CanIAccess(ns, verb, name, resURL string) bool {
}, },
} }
auth, err := kubernetes.NewForConfig(conn.restConfigOrDie()) rest, err := cfg.RESTConfig()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err) log.Warn().Msgf("Access %s", err)
return false
}
auth, err := kubernetes.NewForConfig(rest)
if err != nil {
log.Warn().Msgf("Access %s", err)
return false return false
} }
response, err := auth.AuthorizationV1().SelfSubjectAccessReviews().Create(sar) response, err := auth.AuthorizationV1().SelfSubjectAccessReviews().Create(sar)
if err != nil { if err != nil {
log.Warn().Msgf("%s", err) log.Warn().Msgf("Access %s", err)
return false return false
} }

View File

@ -1,13 +1,12 @@
package k8s package k8s
import ( import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" 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" metricsapi "k8s.io/metrics/pkg/apis/metrics"
versioned "k8s.io/metrics/pkg/client/clientset/versioned" versioned "k8s.io/metrics/pkg/client/clientset/versioned"
) )
@ -15,10 +14,7 @@ import (
// NA Not available // NA Not available
const NA = "n/a" const NA = "n/a"
var ( var supportedMetricsAPIVersions = []string{"v1beta1"}
conn = &apiServer{}
supportedMetricsAPIVersions = []string{"v1beta1"}
)
type ( type (
// APIGroup represents a K8s resource descriptor. // APIGroup represents a K8s resource descriptor.
@ -32,81 +28,125 @@ type (
// Collection of empty interfaces. // Collection of empty interfaces.
Collection []interface{} Collection []interface{}
// Res K8s api server calls. // Cruder represent a crudable Kubernetes resource.
Res interface { Cruder interface {
Get(ns string, name string) (interface{}, error) Get(ns string, name string) (interface{}, error)
List(ns string) (Collection, error) List(ns string) (Collection, error)
Delete(ns string, name string) error Delete(ns string, name string) error
} }
// Connection represents a k8s api server connection. // Connection represents a Kubenetes apiserver connection.
connection interface { Connection interface {
configAccess() clientcmd.ConfigAccess Config() *Config
restConfigOrDie() *restclient.Config DialOrDie() kubernetes.Interface
apiConfigOrDie() clientcmdapi.Config SwitchContextOrDie(ctx string)
dialOrDie() kubernetes.Interface NSDialOrDie() dynamic.NamespaceableResourceInterface
dynDialOrDie() dynamic.Interface RestConfigOrDie() *restclient.Config
nsDialOrDie() dynamic.NamespaceableResourceInterface MXDial() (*versioned.Clientset, error)
mxsDial() (*versioned.Clientset, error) DynDialOrDie() dynamic.Interface
heapsterDial() (*metricsutil.HeapsterMetricsClient, error) HasMetrics() bool
hasMetricsServer() bool IsNamespaced(n string) bool
SupportsResource(group string) bool
} }
apiServer struct { // APIClient represents a Kubernetes api client.
APIClient struct {
config *Config config *Config
client kubernetes.Interface client kubernetes.Interface
dClient dynamic.Interface dClient dynamic.Interface
nsClient dynamic.NamespaceableResourceInterface nsClient dynamic.NamespaceableResourceInterface
heapsterClient *metricsutil.HeapsterMetricsClient
mxsClient *versioned.Clientset mxsClient *versioned.Clientset
useMetricServer bool useMetricServer bool
log zerolog.Logger
} }
) )
// InitConnectionOrDie initialize connection from command line args. // InitConnectionOrDie initialize connection from command line args.
// Checks for connectivity with the api server. // Checks for connectivity with the api server.
func InitConnectionOrDie(config *Config) { func InitConnectionOrDie(config *Config, logger zerolog.Logger) *APIClient {
conn = &apiServer{config: config} conn := APIClient{config: config, log: logger}
conn.useMetricServer = conn.supportsMxServer() conn.useMetricServer = conn.supportsMxServer()
return &conn
} }
func (a *apiServer) hasMetricsServer() bool { // IsNamespaced check on server if given resource is namespaced
func (a *APIClient) IsNamespaced(res string) bool {
list, _ := a.DialOrDie().Discovery().ServerPreferredResources()
for _, l := range list {
log.Debug().Msgf("GV %s", l.GroupVersion)
for _, r := range l.APIResources {
if r.Name == res {
return r.Namespaced
}
}
}
return false
}
// SupportsResource checks for resource supported version against the server.
func (a *APIClient) SupportsResource(group string) bool {
list, _ := a.DialOrDie().Discovery().ServerPreferredResources()
for _, l := range list {
if l.GroupVersion == group {
return true
}
}
return false
}
// Config return a kubernetes configuration.
func (a *APIClient) Config() *Config {
return a.config
}
// HasMetrics returns true if the cluster supports metrics.
func (a *APIClient) HasMetrics() bool {
return a.useMetricServer return a.useMetricServer
} }
// DialOrDie returns a handle to api server or die. // DialOrDie returns a handle to api server or die.
func (a *apiServer) dialOrDie() kubernetes.Interface { func (a *APIClient) DialOrDie() kubernetes.Interface {
if a.client != nil { if a.client != nil {
return a.client return a.client
} }
var err error var err error
if a.client, err = kubernetes.NewForConfig(a.restConfigOrDie()); err != nil { if a.client, err = kubernetes.NewForConfig(a.RestConfigOrDie()); err != nil {
panic(err) a.log.Panic().Err(err)
} }
return a.client return a.client
} }
// DynDial returns a handle to the api server. // RestConfigOrDie returns a rest api client.
func (a *apiServer) dynDialOrDie() dynamic.Interface { func (a *APIClient) RestConfigOrDie() *restclient.Config {
cfg, err := a.config.RESTConfig()
if err != nil {
a.log.Panic().Err(err)
}
return cfg
}
// DynDialOrDie returns a handle to a dynamic interface.
func (a *APIClient) DynDialOrDie() dynamic.Interface {
if a.dClient != nil { if a.dClient != nil {
return a.dClient return a.dClient
} }
var err error var err error
if a.dClient, err = dynamic.NewForConfig(a.restConfigOrDie()); err != nil { if a.dClient, err = dynamic.NewForConfig(a.RestConfigOrDie()); err != nil {
panic(err) a.log.Panic().Err(err)
} }
return a.dClient return a.dClient
} }
func (a *apiServer) nsDialOrDie() dynamic.NamespaceableResourceInterface { // NSDialOrDie returns a handle to a namespaced resource.
func (a *APIClient) NSDialOrDie() dynamic.NamespaceableResourceInterface {
if a.nsClient != nil { if a.nsClient != nil {
return a.nsClient return a.nsClient
} }
a.nsClient = a.dynDialOrDie().Resource(schema.GroupVersionResource{ a.nsClient = a.DynDialOrDie().Resource(schema.GroupVersionResource{
Group: "apiextensions.k8s.io", Group: "apiextensions.k8s.io",
Version: "v1beta1", Version: "v1beta1",
Resource: "customresourcedefinitions", Resource: "customresourcedefinitions",
@ -114,40 +154,21 @@ func (a *apiServer) nsDialOrDie() dynamic.NamespaceableResourceInterface {
return a.nsClient return a.nsClient
} }
func (a *apiServer) heapsterDial() (*metricsutil.HeapsterMetricsClient, error) { // MXDial returns a handle to the metrics server.
if a.heapsterClient != nil { func (a *APIClient) MXDial() (*versioned.Clientset, error) {
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 { if a.mxsClient != nil {
return a.mxsClient, nil return a.mxsClient, nil
} }
var err error var err error
a.mxsClient, err = versioned.NewForConfig(a.restConfigOrDie()) if a.mxsClient, err = versioned.NewForConfig(a.RestConfigOrDie()); err != nil {
a.log.Debug().Err(err)
}
return a.mxsClient, err return a.mxsClient, err
} }
func (a *apiServer) restConfigOrDie() *restclient.Config { // SwitchContextOrDie handles kubeconfig context switches.
cfg, err := a.config.RESTConfig() func (a *APIClient) SwitchContextOrDie(ctx string) {
if err != nil {
panic(err)
}
return cfg
}
func (a *apiServer) switchContextOrDie(ctx string) {
currentCtx, err := a.config.CurrentContextName() currentCtx, err := a.config.CurrentContextName()
if err != nil { if err != nil {
panic(err) panic(err)
@ -162,13 +183,12 @@ func (a *apiServer) switchContextOrDie(ctx string) {
} }
} }
func (a *apiServer) reset() { func (a *APIClient) reset() {
a.client, a.dClient, a.nsClient = nil, nil, nil a.client, a.dClient, a.nsClient, a.mxsClient = nil, nil, nil, nil
a.heapsterClient, a.mxsClient = nil, nil
} }
func (a *apiServer) supportsMxServer() bool { func (a *APIClient) supportsMxServer() bool {
apiGroups, err := a.dialOrDie().Discovery().ServerGroups() apiGroups, err := a.DialOrDie().Discovery().ServerGroups()
if err != nil { if err != nil {
return false return false
} }
@ -177,6 +197,7 @@ func (a *apiServer) supportsMxServer() bool {
if discoveredAPIGroup.Name != metricsapi.GroupName { if discoveredAPIGroup.Name != metricsapi.GroupName {
continue continue
} }
for _, version := range discoveredAPIGroup.Versions { for _, version := range discoveredAPIGroup.Versions {
for _, supportedVersion := range supportedMetricsAPIVersions { for _, supportedVersion := range supportedMetricsAPIVersions {
if version.Version == supportedVersion { if version.Version == supportedVersion {
@ -185,5 +206,6 @@ func (a *apiServer) supportsMxServer() bool {
} }
} }
} }
return false return false
} }

View File

@ -1,20 +1,26 @@
package k8s package k8s
import "github.com/rs/zerolog/log" import (
"github.com/rs/zerolog"
)
// Cluster represents a Kubernetes cluster. // Cluster represents a Kubernetes cluster.
type Cluster struct{} type Cluster struct {
Connection
logger *zerolog.Logger
}
// NewCluster instantiates a new cluster. // NewCluster instantiates a new cluster.
func NewCluster() *Cluster { func NewCluster(c Connection, l *zerolog.Logger) *Cluster {
return &Cluster{} return &Cluster{c, l}
} }
// Version returns the current cluster git version. // Version returns the current cluster git version.
func (c *Cluster) Version() (string, error) { func (c *Cluster) Version() (string, error) {
rev, err := conn.dialOrDie().Discovery().ServerVersion() rev, err := c.DialOrDie().Discovery().ServerVersion()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err) c.logger.Warn().Msgf("%s", err)
return "", err return "", err
} }
return rev.GitVersion, nil return rev.GitVersion, nil
@ -22,9 +28,9 @@ func (c *Cluster) Version() (string, error) {
// ContextName returns the currently active context. // ContextName returns the currently active context.
func (c *Cluster) ContextName() string { func (c *Cluster) ContextName() string {
ctx, err := conn.config.CurrentContextName() ctx, err := c.Config().CurrentContextName()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err) c.logger.Warn().Msgf("%s", err)
return "N/A" return "N/A"
} }
return ctx return ctx
@ -32,9 +38,9 @@ func (c *Cluster) ContextName() string {
// ClusterName return the currently active cluster name. // ClusterName return the currently active cluster name.
func (c *Cluster) ClusterName() string { func (c *Cluster) ClusterName() string {
ctx, err := conn.config.CurrentClusterName() ctx, err := c.Config().CurrentClusterName()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err) c.logger.Warn().Msgf("%s", err)
return "N/A" return "N/A"
} }
return ctx return ctx
@ -42,9 +48,9 @@ func (c *Cluster) ClusterName() string {
// UserName returns the currently active user. // UserName returns the currently active user.
func (c *Cluster) UserName() string { func (c *Cluster) UserName() string {
usr, err := conn.config.CurrentUserName() usr, err := c.Config().CurrentUserName()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err) c.logger.Warn().Msgf("%s", err)
return "N/A" return "N/A"
} }
return usr return usr

View File

@ -5,28 +5,26 @@ import (
) )
// ClusterRole represents a Kubernetes ClusterRole // ClusterRole represents a Kubernetes ClusterRole
type ClusterRole struct{} type ClusterRole struct {
Connection
}
// NewClusterRole returns a new ClusterRole. // NewClusterRole returns a new ClusterRole.
func NewClusterRole() Res { func NewClusterRole(c Connection) Cruder {
return &ClusterRole{} return &ClusterRole{c}
} }
// Get a cluster role. // Get a cluster role.
func (*ClusterRole) Get(_, n string) (interface{}, error) { func (c *ClusterRole) Get(_, n string) (interface{}, error) {
opts := metav1.GetOptions{} return c.DialOrDie().RbacV1().ClusterRoles().Get(n, metav1.GetOptions{})
return conn.dialOrDie().RbacV1().ClusterRoles().Get(n, opts)
} }
// List all ClusterRoles on a cluster. // List all ClusterRoles on a cluster.
func (*ClusterRole) List(_ string) (Collection, error) { func (c *ClusterRole) List(_ string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := c.DialOrDie().RbacV1().ClusterRoles().List(metav1.ListOptions{})
rr, err := conn.dialOrDie().RbacV1().ClusterRoles().List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*ClusterRole) List(_ string) (Collection, error) {
} }
// Delete a ClusterRole. // Delete a ClusterRole.
func (*ClusterRole) Delete(_, n string) error { func (c *ClusterRole) Delete(_, n string) error {
opts := metav1.DeleteOptions{} return c.DialOrDie().RbacV1().ClusterRoles().Delete(n, nil)
return conn.dialOrDie().RbacV1().ClusterRoles().Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// ClusterRoleBinding represents a Kubernetes ClusterRoleBinding // ClusterRoleBinding represents a Kubernetes ClusterRoleBinding
type ClusterRoleBinding struct{} type ClusterRoleBinding struct {
Connection
}
// NewClusterRoleBinding returns a new ClusterRoleBinding. // NewClusterRoleBinding returns a new ClusterRoleBinding.
func NewClusterRoleBinding() Res { func NewClusterRoleBinding(c Connection) Cruder {
return &ClusterRoleBinding{} return &ClusterRoleBinding{c}
} }
// Get a service. // Get a service.
func (*ClusterRoleBinding) Get(_, n string) (interface{}, error) { func (c *ClusterRoleBinding) Get(_, n string) (interface{}, error) {
opts := metav1.GetOptions{} return c.DialOrDie().RbacV1().ClusterRoleBindings().Get(n, metav1.GetOptions{})
return conn.dialOrDie().RbacV1().ClusterRoleBindings().Get(n, opts)
} }
// List all ClusterRoleBindings on a cluster. // List all ClusterRoleBindings on a cluster.
func (*ClusterRoleBinding) List(_ string) (Collection, error) { func (c *ClusterRoleBinding) List(_ string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := c.DialOrDie().RbacV1().ClusterRoleBindings().List(metav1.ListOptions{})
rr, err := conn.dialOrDie().RbacV1().ClusterRoleBindings().List(opts)
if err != nil { if err != nil {
return Collection{}, err return Collection{}, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*ClusterRoleBinding) List(_ string) (Collection, error) {
} }
// Delete a ClusterRoleBinding. // Delete a ClusterRoleBinding.
func (*ClusterRoleBinding) Delete(_, n string) error { func (c *ClusterRoleBinding) Delete(_, n string) error {
opts := metav1.DeleteOptions{} return c.DialOrDie().RbacV1().ClusterRoleBindings().Delete(n, nil)
return conn.dialOrDie().RbacV1().ClusterRoleBindings().Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// ConfigMap represents a Kubernetes ConfigMap // ConfigMap represents a Kubernetes ConfigMap
type ConfigMap struct{} type ConfigMap struct {
Connection
}
// NewConfigMap returns a new ConfigMap. // NewConfigMap returns a new ConfigMap.
func NewConfigMap() Res { func NewConfigMap(c Connection) Cruder {
return &ConfigMap{} return &ConfigMap{c}
} }
// Get a ConfigMap. // Get a ConfigMap.
func (c *ConfigMap) Get(ns, n string) (interface{}, error) { func (c *ConfigMap) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return c.DialOrDie().CoreV1().ConfigMaps(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().ConfigMaps(ns).Get(n, opts)
} }
// List all ConfigMaps in a given namespace. // List all ConfigMaps in a given namespace.
func (c *ConfigMap) List(ns string) (Collection, error) { func (c *ConfigMap) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := c.DialOrDie().CoreV1().ConfigMaps(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().ConfigMaps(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -37,6 +35,5 @@ func (c *ConfigMap) List(ns string) (Collection, error) {
// Delete a ConfigMap. // Delete a ConfigMap.
func (c *ConfigMap) Delete(ns, n string) error { func (c *ConfigMap) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return c.DialOrDie().CoreV1().ConfigMaps(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().ConfigMaps(ns).Delete(n, &opts)
} }

View File

@ -14,9 +14,6 @@ import (
const defaultNamespace = "default" const defaultNamespace = "default"
// KubeConfig represents kubeconfig settings.
var KubeConfig *Config
// Config tracks a kubernetes configuration. // Config tracks a kubernetes configuration.
type Config struct { type Config struct {
flags *genericclioptions.ConfigFlags flags *genericclioptions.ConfigFlags
@ -28,8 +25,7 @@ type Config struct {
// NewConfig returns a new k8s config or an error if the flags are invalid. // NewConfig returns a new k8s config or an error if the flags are invalid.
func NewConfig(f *genericclioptions.ConfigFlags) *Config { func NewConfig(f *genericclioptions.ConfigFlags) *Config {
KubeConfig = &Config{flags: f} return &Config{flags: f}
return KubeConfig
} }
// Flags returns configuration flags. // Flags returns configuration flags.
@ -60,6 +56,7 @@ func (c *Config) CurrentContextName() (string, error) {
if isSet(c.flags.Context) { if isSet(c.flags.Context) {
return *c.flags.Context, nil return *c.flags.Context, nil
} }
cfg, err := c.RawConfig() cfg, err := c.RawConfig()
if err != nil { if err != nil {
return "", err return "", err
@ -82,11 +79,11 @@ func (c *Config) GetContext(n string) (*clientcmdapi.Context, error) {
// Contexts fetch all available contexts. // Contexts fetch all available contexts.
func (c *Config) Contexts() (map[string]*clientcmdapi.Context, error) { func (c *Config) Contexts() (map[string]*clientcmdapi.Context, error) {
var cc map[string]*clientcmdapi.Context
cfg, err := c.RawConfig() cfg, err := c.RawConfig()
if err != nil { if err != nil {
return cc, err return nil, err
} }
return cfg.Contexts, nil return cfg.Contexts, nil
} }
@ -97,18 +94,18 @@ func (c *Config) DelContext(n string) error {
return err return err
} }
delete(cfg.Contexts, n) delete(cfg.Contexts, n)
return clientcmd.ModifyConfig(c.clientConfig.ConfigAccess(), cfg, true) return clientcmd.ModifyConfig(c.clientConfig.ConfigAccess(), cfg, true)
} }
// ContextNames fetch all available contexts. // ContextNames fetch all available contexts.
func (c *Config) ContextNames() ([]string, error) { func (c *Config) ContextNames() ([]string, error) {
var cc []string
cfg, err := c.RawConfig() cfg, err := c.RawConfig()
if err != nil { if err != nil {
return cc, err return nil, err
} }
cc = make([]string, 0, len(cfg.Contexts)) cc := make([]string, 0, len(cfg.Contexts))
for n := range cfg.Contexts { for n := range cfg.Contexts {
cc = append(cc, n) cc = append(cc, n)
} }
@ -121,6 +118,7 @@ func (c *Config) ClusterNameFromContext(ctx string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
if ctx, ok := cfg.Contexts[ctx]; ok { if ctx, ok := cfg.Contexts[ctx]; ok {
return ctx.Cluster, nil return ctx.Cluster, nil
} }
@ -137,32 +135,31 @@ func (c *Config) CurrentClusterName() (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
current := cfg.CurrentContext current := cfg.CurrentContext
if isSet(c.flags.Context) { if isSet(c.flags.Context) {
current = *c.flags.Context current = *c.flags.Context
} }
if ctx, ok := cfg.Contexts[current]; ok { if ctx, ok := cfg.Contexts[current]; ok {
return ctx.Cluster, nil return ctx.Cluster, nil
} }
return "", errors.New("unable to locate current cluster") return "", errors.New("unable to locate current cluster")
} }
// ClusterNames fetch all kubeconfig defined clusters. // ClusterNames fetch all kubeconfig defined clusters.
func (c *Config) ClusterNames() ([]string, error) { func (c *Config) ClusterNames() ([]string, error) {
var cc []string
if err := c.configFromFlags(); err != nil {
return cc, err
}
cfg, err := c.RawConfig() cfg, err := c.RawConfig()
if err != nil { if err != nil {
return cc, err return nil, err
} }
cc = make([]string, 0, len(cfg.Clusters)) cc := make([]string, 0, len(cfg.Clusters))
for name := range cfg.Clusters { for name := range cfg.Clusters {
cc = append(cc, name) cc = append(cc, name)
} }
return cc, nil return cc, nil
} }
@ -171,6 +168,7 @@ func (c *Config) CurrentUserName() (string, error) {
if isSet(c.flags.Impersonate) { if isSet(c.flags.Impersonate) {
return *c.flags.Impersonate, nil return *c.flags.Impersonate, nil
} }
if isSet(c.flags.AuthInfoName) { if isSet(c.flags.AuthInfoName) {
return *c.flags.AuthInfoName, nil return *c.flags.AuthInfoName, nil
} }
@ -187,6 +185,7 @@ func (c *Config) CurrentUserName() (string, error) {
if ctx, ok := cfg.Contexts[current]; ok { if ctx, ok := cfg.Contexts[current]; ok {
return ctx.AuthInfo, nil return ctx.AuthInfo, nil
} }
return "", errors.New("unable to locate current cluster") return "", errors.New("unable to locate current cluster")
} }
@ -211,48 +210,39 @@ func (c *Config) CurrentNamespaceName() (string, error) {
return ctx.Namespace, nil return ctx.Namespace, nil
} }
} }
return "", fmt.Errorf("No active namespace specified") return "", fmt.Errorf("No active namespace specified")
} }
// NamespaceNames fetch all available namespaces on current cluster. // NamespaceNames fetch all available namespaces on current cluster.
func (c *Config) NamespaceNames() ([]string, error) { func (c *Config) NamespaceNames(nns []v1.Namespace) []string {
ll, err := NewNamespace().List("") nn := make([]string, 0, len(nns))
if err != nil { for _, ns := range nns {
return []string{}, err nn = append(nn, ns.Name)
} }
nn := make([]string, 0, len(ll))
for _, n := range ll { return nn
if ns, ok := n.(v1.Namespace); ok {
nn = append(nn, ns.Name)
}
}
return nn, nil
} }
// ConfigAccess return the current kubeconfig api server access configuration. // ConfigAccess return the current kubeconfig api server access configuration.
func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) { func (c *Config) ConfigAccess() (clientcmd.ConfigAccess, error) {
var acc clientcmd.ConfigAccess c.ensureConfig()
if err := c.configFromFlags(); err != nil {
return acc, err
}
return c.clientConfig.ConfigAccess(), nil return c.clientConfig.ConfigAccess(), nil
} }
// RawConfig fetch the current kubeconfig with no overrides. // RawConfig fetch the current kubeconfig with no overrides.
func (c *Config) RawConfig() (clientcmdapi.Config, error) { func (c *Config) RawConfig() (clientcmdapi.Config, error) {
if c.rawConfig != nil { if c.rawConfig != nil {
if c.rawConfig.CurrentContext != c.currentContext { if c.rawConfig.CurrentContext == c.currentContext {
log.Debug().Msgf("Context switch detected... %s vs %s", c.rawConfig.CurrentContext, c.currentContext) return *c.rawConfig, nil
c.currentContext = c.rawConfig.CurrentContext
c.reset()
} }
log.Debug().Msgf("Context switch detected... %s vs %s", c.rawConfig.CurrentContext, c.currentContext)
c.currentContext = c.rawConfig.CurrentContext
c.reset()
} }
if c.rawConfig == nil { if c.rawConfig == nil {
if err := c.configFromFlags(); err != nil { c.ensureConfig()
return clientcmdapi.Config{}, err
}
log.Debug().Msg("Loading RawConfig...")
cfg, err := c.clientConfig.RawConfig() cfg, err := c.clientConfig.RawConfig()
if err != nil { if err != nil {
return cfg, err return cfg, err
@ -260,36 +250,38 @@ func (c *Config) RawConfig() (clientcmdapi.Config, error) {
c.rawConfig = &cfg c.rawConfig = &cfg
c.currentContext = cfg.CurrentContext c.currentContext = cfg.CurrentContext
} }
return *c.rawConfig, nil return *c.rawConfig, nil
} }
// RESTConfig fetch the current REST api service connection. // RESTConfig fetch the current REST api service connection.
func (c *Config) RESTConfig() (*restclient.Config, error) { func (c *Config) RESTConfig() (*restclient.Config, error) {
var err error if c.restConfig != nil {
if c.restConfig == nil { return c.restConfig, nil
if err = c.configFromFlags(); err != nil {
return nil, err
}
c.restConfig, err = c.flags.ToRESTConfig()
if err != nil {
return c.restConfig, err
}
log.Debug().Msgf("Connecting to API Server %s", c.restConfig.Host)
} }
var err error
if c.restConfig, err = c.flags.ToRESTConfig(); err != nil {
return nil, err
}
log.Debug().Msgf("Connecting to API Server %s", c.restConfig.Host)
return c.restConfig, nil return c.restConfig, nil
} }
func (c *Config) ensureConfig() {
if c.clientConfig != nil {
return
}
log.Debug().Msg("Loading raw config from flags...")
c.clientConfig = c.flags.ToRawKubeConfigLoader()
return
}
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Helpers... // Helpers...
func (c *Config) configFromFlags() error {
if c.clientConfig == nil {
log.Debug().Msg("Loading raw config from flags...")
c.clientConfig = c.flags.ToRawKubeConfigLoader()
}
return nil
}
func isSet(s *string) bool { func isSet(s *string) bool {
return s != nil && len(*s) != 0 return s != nil && len(*s) != 0
} }

View File

@ -8,6 +8,8 @@ import (
"github.com/derailed/k9s/internal/k8s" "github.com/derailed/k9s/internal/k8s"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
) )
@ -228,3 +230,22 @@ func TestConfigBadConfig(t *testing.T) {
_, err := cfg.RESTConfig() _, err := cfg.RESTConfig()
assert.NotNil(t, err) assert.NotNil(t, err)
} }
func TestNamespaceNames(t *testing.T) {
kubeConfig := "./assets/config"
flags := genericclioptions.ConfigFlags{
KubeConfig: &kubeConfig,
}
cfg := k8s.NewConfig(&flags)
nn := []v1.Namespace{
{ObjectMeta: metav1.ObjectMeta{Name: "ns1"}},
{ObjectMeta: metav1.ObjectMeta{Name: "ns2"}},
}
nns := cfg.NamespaceNames(nn)
assert.Equal(t, 2, len(nns))
assert.Equal(t, []string{"ns1", "ns2"}, nns)
}

View File

@ -9,7 +9,7 @@ import (
// ContextRes represents a Kubernetes clusters configurations. // ContextRes represents a Kubernetes clusters configurations.
type ContextRes interface { type ContextRes interface {
Res Cruder
Switch(n string) error Switch(n string) error
} }
@ -17,11 +17,17 @@ type ContextRes interface {
type NamedContext struct { type NamedContext struct {
Name string Name string
Context *api.Context Context *api.Context
config *Config
}
// NewNamedContext returns a new named context.
func NewNamedContext(c *Config, n string, ctx *api.Context) *NamedContext {
return &NamedContext{Name: n, Context: ctx, config: c}
} }
// MustCurrentContextName return the active context name. // MustCurrentContextName return the active context name.
func (c *NamedContext) MustCurrentContextName() string { func (c *NamedContext) MustCurrentContextName() string {
cl, err := conn.config.CurrentContextName() cl, err := c.config.CurrentContextName()
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -29,16 +35,18 @@ func (c *NamedContext) MustCurrentContextName() string {
} }
// Context represents a Kubernetes Context. // Context represents a Kubernetes Context.
type Context struct{} type Context struct {
Connection
}
// NewContext returns a new Context. // NewContext returns a new Context.
func NewContext() Res { func NewContext(c Connection) Cruder {
return &Context{} return &Context{c}
} }
// Get a Context. // Get a Context.
func (*Context) Get(_, n string) (interface{}, error) { func (c *Context) Get(_, n string) (interface{}, error) {
ctx, err := conn.config.GetContext(n) ctx, err := c.Config().GetContext(n)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -46,43 +54,45 @@ func (*Context) Get(_, n string) (interface{}, error) {
} }
// List all Contexts on the current cluster. // List all Contexts on the current cluster.
func (*Context) List(string) (Collection, error) { func (c *Context) List(string) (Collection, error) {
ctxs, err := conn.config.Contexts() ctxs, err := c.Config().Contexts()
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make([]interface{}, 0, len(ctxs)) cc := make([]interface{}, 0, len(ctxs))
for k, v := range ctxs { for k, v := range ctxs {
cc = append(cc, &NamedContext{k, v}) cc = append(cc, NewNamedContext(c.Config(), k, v))
} }
return cc, nil return cc, nil
} }
// Delete a Context. // Delete a Context.
func (*Context) Delete(_, n string) error { func (c *Context) Delete(_, n string) error {
ctx, err := conn.config.CurrentContextName() ctx, err := c.Config().CurrentContextName()
if err != nil { if err != nil {
return err return err
} }
if ctx == n { if ctx == n {
return fmt.Errorf("trying to delete your current context %s", n) return fmt.Errorf("trying to delete your current context %s", n)
} }
return conn.config.DelContext(n) return c.Config().DelContext(n)
} }
// Switch to another context. // Switch to another context.
func (*Context) Switch(n string) error { func (c *Context) Switch(ctx string) error {
conn.switchContextOrDie(n) c.SwitchContextOrDie(ctx)
return nil return nil
} }
// KubeUpdate modifies kubeconfig default context. // KubeUpdate modifies kubeconfig default context.
func (c *Context) KubeUpdate(n string) error { func (c *Context) KubeUpdate(n string) error {
c.Switch(n) config, err := c.Config().RawConfig()
acc := clientcmd.NewDefaultPathOptions()
config, err := conn.config.RawConfig()
if err != nil { if err != nil {
return err return err
} }
return clientcmd.ModifyConfig(acc, config, true) c.Switch(n)
return clientcmd.ModifyConfig(
clientcmd.NewDefaultPathOptions(), config, true,
)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// CRD represents a Kubernetes CRD // CRD represents a Kubernetes CRD
type CRD struct{} type CRD struct {
Connection
}
// NewCRD returns a new CRD. // NewCRD returns a new CRD.
func NewCRD() Res { func NewCRD(c Connection) Cruder {
return &CRD{} return &CRD{c}
} }
// Get a CRD. // Get a CRD.
func (*CRD) Get(_, n string) (interface{}, error) { func (c *CRD) Get(_, n string) (interface{}, error) {
opts := metav1.GetOptions{} return c.NSDialOrDie().Get(n, metav1.GetOptions{})
return conn.nsDialOrDie().Get(n, opts)
} }
// List all CRDs in a given namespace. // List all CRDs in a given namespace.
func (*CRD) List(string) (Collection, error) { func (c *CRD) List(string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := c.NSDialOrDie().List(metav1.ListOptions{})
rr, err := conn.nsDialOrDie().List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*CRD) List(string) (Collection, error) {
} }
// Delete a CRD. // Delete a CRD.
func (*CRD) Delete(_, n string) error { func (c *CRD) Delete(_, n string) error {
opts := metav1.DeleteOptions{} return c.NSDialOrDie().Delete(n, nil)
return conn.nsDialOrDie().Delete(n, &opts)
} }

View File

@ -10,28 +10,26 @@ import (
const maxJobNameSize = 42 const maxJobNameSize = 42
// CronJob represents a Kubernetes CronJob. // CronJob represents a Kubernetes CronJob.
type CronJob struct{} type CronJob struct {
Connection
}
// NewCronJob returns a new CronJob. // NewCronJob returns a new CronJob.
func NewCronJob() Res { func NewCronJob(c Connection) Cruder {
return &CronJob{} return &CronJob{c}
} }
// Get a CronJob. // Get a CronJob.
func (c *CronJob) Get(ns, n string) (interface{}, error) { func (c *CronJob) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return c.DialOrDie().BatchV1beta1().CronJobs(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().BatchV1beta1().CronJobs(ns).Get(n, opts)
} }
// List all CronJobs in a given namespace. // List all CronJobs in a given namespace.
func (c *CronJob) List(ns string) (Collection, error) { func (c *CronJob) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := c.DialOrDie().BatchV1beta1().CronJobs(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().BatchV1beta1().CronJobs(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -42,22 +40,20 @@ func (c *CronJob) List(ns string) (Collection, error) {
// Delete a CronJob. // Delete a CronJob.
func (c *CronJob) Delete(ns, n string) error { func (c *CronJob) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return c.DialOrDie().BatchV1beta1().CronJobs(ns).Delete(n, nil)
return conn.dialOrDie().BatchV1beta1().CronJobs(ns).Delete(n, &opts)
} }
// Run the job associated with this cronjob. // Run the job associated with this cronjob.
func (c *CronJob) Run(ns, n string) error { func (c *CronJob) Run(ns, n string) error {
i, err := c.Get(ns, n) cj, err := c.Get(ns, n)
if err != nil { if err != nil {
return err return err
} }
cronJob := cj.(*batchv1beta1.CronJob)
cronJob := i.(*batchv1beta1.CronJob)
var jobName = cronJob.Name var jobName = cronJob.Name
if len(cronJob.Name) >= maxJobNameSize { if len(cronJob.Name) >= maxJobNameSize {
jobName = cronJob.Name[0:41] jobName = cronJob.Name[0:maxJobNameSize]
} }
job := &batchv1.Job{ job := &batchv1.Job{
@ -68,6 +64,7 @@ func (c *CronJob) Run(ns, n string) error {
}, },
Spec: cronJob.Spec.JobTemplate.Spec, Spec: cronJob.Spec.JobTemplate.Spec,
} }
_, err = conn.dialOrDie().BatchV1().Jobs(ns).Create(job)
_, err = c.DialOrDie().BatchV1().Jobs(ns).Create(job)
return err return err
} }

View File

@ -5,28 +5,26 @@ import (
) )
// Deployment represents a Kubernetes Deployment. // Deployment represents a Kubernetes Deployment.
type Deployment struct{} type Deployment struct {
Connection
}
// NewDeployment returns a new Deployment. // NewDeployment returns a new Deployment.
func NewDeployment() Res { func NewDeployment(c Connection) Cruder {
return &Deployment{} return &Deployment{c}
} }
// Get a deployment. // Get a deployment.
func (*Deployment) Get(ns, n string) (interface{}, error) { func (d *Deployment) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return d.DialOrDie().Apps().Deployments(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().Apps().Deployments(ns).Get(n, opts)
} }
// List all Deployments in a given namespace. // List all Deployments in a given namespace.
func (*Deployment) List(ns string) (Collection, error) { func (d *Deployment) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := d.DialOrDie().Apps().Deployments(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().Apps().Deployments(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*Deployment) List(ns string) (Collection, error) {
} }
// Delete a Deployment. // Delete a Deployment.
func (*Deployment) Delete(ns, n string) error { func (d *Deployment) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return d.DialOrDie().Apps().Deployments(ns).Delete(n, nil)
return conn.dialOrDie().Apps().Deployments(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// DaemonSet represents a Kubernetes DaemonSet // DaemonSet represents a Kubernetes DaemonSet
type DaemonSet struct{} type DaemonSet struct {
Connection
}
// NewDaemonSet returns a new DaemonSet. // NewDaemonSet returns a new DaemonSet.
func NewDaemonSet() Res { func NewDaemonSet(c Connection) Cruder {
return &DaemonSet{} return &DaemonSet{c}
} }
// Get a DaemonSet. // Get a DaemonSet.
func (*DaemonSet) Get(ns, n string) (interface{}, error) { func (d *DaemonSet) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return d.DialOrDie().ExtensionsV1beta1().DaemonSets(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().ExtensionsV1beta1().DaemonSets(ns).Get(n, opts)
} }
// List all DaemonSets in a given namespace. // List all DaemonSets in a given namespace.
func (*DaemonSet) List(ns string) (Collection, error) { func (d *DaemonSet) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := d.DialOrDie().ExtensionsV1beta1().DaemonSets(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().ExtensionsV1beta1().DaemonSets(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*DaemonSet) List(ns string) (Collection, error) {
} }
// Delete a DaemonSet. // Delete a DaemonSet.
func (*DaemonSet) Delete(ns, n string) error { func (d *DaemonSet) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return d.DialOrDie().ExtensionsV1beta1().DaemonSets(ns).Delete(n, nil)
return conn.dialOrDie().ExtensionsV1beta1().DaemonSets(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// Endpoints represents a Kubernetes Endpoints. // Endpoints represents a Kubernetes Endpoints.
type Endpoints struct{} type Endpoints struct {
Connection
}
// NewEndpoints returns a new Endpoints. // NewEndpoints returns a new Endpoints.
func NewEndpoints() Res { func NewEndpoints(c Connection) Cruder {
return &Endpoints{} return &Endpoints{c}
} }
// Get a Endpoint. // Get a Endpoint.
func (*Endpoints) Get(ns, n string) (interface{}, error) { func (e *Endpoints) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return e.DialOrDie().CoreV1().Endpoints(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Endpoints(ns).Get(n, opts)
} }
// List all Endpoints in a given namespace. // List all Endpoints in a given namespace.
func (*Endpoints) List(ns string) (Collection, error) { func (e *Endpoints) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := e.DialOrDie().CoreV1().Endpoints(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Endpoints(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*Endpoints) List(ns string) (Collection, error) {
} }
// Delete a Endpoint. // Delete a Endpoint.
func (*Endpoints) Delete(ns, n string) error { func (e *Endpoints) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return e.DialOrDie().CoreV1().Endpoints(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().Endpoints(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// Event represents a Kubernetes Event. // Event represents a Kubernetes Event.
type Event struct{} type Event struct {
Connection
}
// NewEvent returns a new Event. // NewEvent returns a new Event.
func NewEvent() Res { func NewEvent(c Connection) Cruder {
return &Event{} return &Event{c}
} }
// Get a Event. // Get a Event.
func (*Event) Get(ns, n string) (interface{}, error) { func (e *Event) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return e.DialOrDie().CoreV1().Events(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Events(ns).Get(n, opts)
} }
// List all Events in a given namespace. // List all Events in a given namespace.
func (*Event) List(ns string) (Collection, error) { func (e *Event) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := e.DialOrDie().CoreV1().Events(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Events(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*Event) List(ns string) (Collection, error) {
} }
// Delete an Event. // Delete an Event.
func (*Event) Delete(ns, n string) error { func (e *Event) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return e.DialOrDie().CoreV1().Events(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().Events(ns).Delete(n, &opts)
} }

18
internal/k8s/helpers.go Normal file
View File

@ -0,0 +1,18 @@
package k8s
import (
"math"
)
const megaByte = 1024 * 1024
func asMi(v int64) float64 {
return float64(v) / megaByte
}
func toPerc(v1, v2 float64) float64 {
if v2 == 0 {
return 0
}
return math.Round((v1 / v2) * 100)
}

View File

@ -0,0 +1,36 @@
package k8s
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestToPerc(t *testing.T) {
uu := []struct {
v1, v2, e float64
}{
{0, 0, 0},
{100, 200, 50},
{200, 100, 200},
}
for _, u := range uu {
assert.Equal(t, u.e, toPerc(u.v1, u.v2))
}
}
func TestAsMi(t *testing.T) {
uu := []struct {
v int64
e float64
}{
{0, 0},
{2 * megaByte, 2},
{10 * megaByte, 10},
}
for _, u := range uu {
assert.Equal(t, u.e, asMi(u.v))
}
}

View File

@ -5,38 +5,37 @@ import (
) )
// HPA represents am HorizontalPodAutoscaler. // HPA represents am HorizontalPodAutoscaler.
type HPA struct{} type HPA struct {
Connection
}
// NewHPA returns a new HPA. // NewHPA returns a new HPA.
func NewHPA() Res { func NewHPA(c Connection) Cruder {
return &HPA{} return &HPA{c}
} }
// Get a HPA. // Get a HPA.
func (*HPA) Get(ns, n string) (interface{}, error) { func (h *HPA) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, opts)
} }
// List all HPAs in a given namespace. // List all HPAs in a given namespace.
func (*HPA) List(ns string) (Collection, error) { func (h *HPA) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
} }
return cc, nil return cc, nil
} }
// Delete a HPA. // Delete a HPA.
func (*HPA) Delete(ns, n string) error { func (h *HPA) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} if h.SupportsResource("autoscaling/v2beta1") {
return conn.dialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Delete(n, &opts) return h.DialOrDie().AutoscalingV2beta1().HorizontalPodAutoscalers(ns).Delete(n, nil)
}
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Delete(n, nil)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// Ingress represents a Kubernetes Ingress. // Ingress represents a Kubernetes Ingress.
type Ingress struct{} type Ingress struct {
Connection
}
// NewIngress returns a new Ingress. // NewIngress returns a new Ingress.
func NewIngress() Res { func NewIngress(c Connection) Cruder {
return &Ingress{} return &Ingress{c}
} }
// Get a Ingress. // Get a Ingress.
func (*Ingress) Get(ns, n string) (interface{}, error) { func (i *Ingress) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().ExtensionsV1beta1().Ingresses(ns).Get(n, opts)
} }
// List all Ingresss in a given namespace. // List all Ingresss in a given namespace.
func (*Ingress) List(ns string) (Collection, error) { func (i *Ingress) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().ExtensionsV1beta1().Ingresses(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*Ingress) List(ns string) (Collection, error) {
} }
// Delete a Ingress. // Delete a Ingress.
func (*Ingress) Delete(ns, n string) error { func (i *Ingress) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).Delete(n, nil)
return conn.dialOrDie().ExtensionsV1beta1().Ingresses(ns).Delete(n, &opts)
} }

View File

@ -4,35 +4,40 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
) )
// Job represents a Kubernetes Job. type (
type Job struct{} // Job represents a Kubernetes Job.
Job struct {
Connection
}
// Loggable represents a K8s resource that has containers and can be logged.
Loggable interface {
Containers(ns, n string, includeInit bool) ([]string, error)
Logs(ns, n, co string, lines int64, previous bool) *restclient.Request
}
)
// NewJob returns a new Job. // NewJob returns a new Job.
func NewJob() Res { func NewJob(c Connection) Cruder {
return &Job{} return &Job{c}
} }
// Get a Job. // Get a Job.
func (*Job) Get(ns, n string) (interface{}, error) { func (j *Job) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return j.DialOrDie().BatchV1().Jobs(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().BatchV1().Jobs(ns).Get(n, opts)
} }
// List all Jobs in a given namespace. // List all Jobs in a given namespace.
func (*Job) List(ns string) (Collection, error) { func (j *Job) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := j.DialOrDie().BatchV1().Jobs(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().BatchV1().Jobs(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -42,9 +47,8 @@ func (*Job) List(ns string) (Collection, error) {
} }
// Delete a Job. // Delete a Job.
func (*Job) Delete(ns, n string) error { func (j *Job) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return j.DialOrDie().BatchV1().Jobs(ns).Delete(n, nil)
return conn.dialOrDie().BatchV1().Jobs(ns).Delete(n, &opts)
} }
// Containers returns all container names on job. // Containers returns all container names on job.
@ -53,8 +57,7 @@ func (j *Job) Containers(ns, n string, includeInit bool) ([]string, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debug().Msgf("Containers found assoc pod %v", pod) return NewPod(j).(Loggable).Containers(ns, pod, includeInit)
return NewPod().(Loggable).Containers(ns, pod, includeInit)
} }
// Logs fetch container logs for a given job and container. // Logs fetch container logs for a given job and container.
@ -63,16 +66,15 @@ func (j *Job) Logs(ns, n, co string, lines int64, prev bool) *restclient.Request
if err != nil { if err != nil {
return nil return nil
} }
return NewPod().(Loggable).Logs(ns, pod, co, lines, prev) return NewPod(j).(Loggable).Logs(ns, pod, co, lines, prev)
} }
// Events retrieved jobs events. // Events retrieved jobs events.
func (*Job) Events(ns, n string) (*v1.EventList, error) { func (j *Job) Events(ns, n string) (*v1.EventList, error) {
e := conn.dialOrDie().Core().Events(ns) e := j.DialOrDie().Core().Events(ns)
sel := e.GetFieldSelector(&n, &ns, nil, nil) return e.List(metav1.ListOptions{
opts := metav1.ListOptions{FieldSelector: sel.String()} FieldSelector: e.GetFieldSelector(&n, &ns, nil, nil).String(),
ee, err := e.List(opts) })
return ee, err
} }
func (j *Job) assocPod(ns, n string) (string, error) { func (j *Job) assocPod(ns, n string) (string, error) {

View File

@ -14,12 +14,17 @@ import (
"k8s.io/client-go/restmapper" "k8s.io/client-go/restmapper"
) )
// RestMapping holds k8s resource mapping var (
// BOZO!! Has to be a better way... // RestMapping holds k8s resource mapping
var RestMapping = &RestMapper{} // BOZO!! Has to be a better way...
RestMapping = &RestMapper{}
toFileName = regexp.MustCompile(`[^(\w/\.)]`)
)
// RestMapper map resource to REST mapping ie kind, group, version. // RestMapper map resource to REST mapping ie kind, group, version.
type RestMapper struct{} type RestMapper struct {
Connection
}
// Find a mapping given a resource name. // Find a mapping given a resource name.
func (*RestMapper) Find(res string) (*meta.RESTMapping, error) { func (*RestMapper) Find(res string) (*meta.RESTMapping, error) {
@ -30,8 +35,8 @@ func (*RestMapper) Find(res string) (*meta.RESTMapping, error) {
} }
// ToRESTMapper map resources to kind, and map kind and version to interfaces for manipulating K8s objects. // ToRESTMapper map resources to kind, and map kind and version to interfaces for manipulating K8s objects.
func (*RestMapper) ToRESTMapper() (meta.RESTMapper, error) { func (r *RestMapper) ToRESTMapper() (meta.RESTMapper, error) {
rc := conn.restConfigOrDie() rc := r.RestConfigOrDie()
httpCacheDir := filepath.Join(mustHomeDir(), ".kube", "http-cache") httpCacheDir := filepath.Join(mustHomeDir(), ".kube", "http-cache")
discCacheDir := filepath.Join(mustHomeDir(), ".kube", "cache", "discovery", toHostDir(rc.Host)) discCacheDir := filepath.Join(mustHomeDir(), ".kube", "cache", "discovery", toHostDir(rc.Host))
@ -45,11 +50,8 @@ func (*RestMapper) ToRESTMapper() (meta.RESTMapper, error) {
return expander, nil return expander, nil
} }
var toFileName = regexp.MustCompile(`[^(\w/\.)]`)
func toHostDir(host string) string { func toHostDir(host string) string {
h := strings.Replace(strings.Replace(host, "https://", "", 1), "http://", "", 1) h := 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
return toFileName.ReplaceAllString(h, "_") return toFileName.ReplaceAllString(h, "_")
} }

View File

@ -1,218 +1,145 @@
package k8s package k8s
import ( import (
"fmt"
"math"
"path"
"github.com/rs/zerolog/log"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" mv1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
metricsapi "k8s.io/metrics/pkg/apis/metrics"
metricsV1beta1api "k8s.io/metrics/pkg/apis/metrics/v1beta1"
) )
type ( type (
// MetricsServer serves cluster metrics for nodes and pods. // MetricsServer serves cluster metrics for nodes and pods.
MetricsServer struct{} MetricsServer struct {
Connection
// Metric tracks resource metrics.
Metric struct {
CPU string
Mem string
AvailCPU string
AvailMem string
} }
// NodeMetrics describes raw node metrics.
NodeMetrics struct {
CurrentCPU int64
CurrentMEM float64
AvailCPU int64
AvailMEM float64
TotalCPU int64
TotalMEM float64
}
// PodMetrics represent an aggregation of all pod containers metrics.
PodMetrics struct {
CurrentCPU int64
CurrentMEM float64
}
// ClusterMetrics summarizes total node metrics as percentages.
ClusterMetrics struct {
PercCPU float64
PercMEM float64
}
// NodesMetrics tracks usage metrics per nodes.
NodesMetrics map[string]NodeMetrics
// PodsMetrics tracks usage metrics per pods.
PodsMetrics map[string]PodMetrics
) )
// NewMetricsServer return a metric server instance. // NewMetricsServer return a metric server instance.
func NewMetricsServer() *MetricsServer { func NewMetricsServer(c Connection) *MetricsServer {
return &MetricsServer{} return &MetricsServer{c}
} }
// NodeMetrics retrieves all nodes metrics // NodesMetrics retrieves metrics for a given set of nodes.
func (m *MetricsServer) NodeMetrics() (Metric, error) { func (m *MetricsServer) NodesMetrics(nodes []v1.Node, metrics []mv1beta1.NodeMetrics, mmx NodesMetrics) {
var mx Metric for _, n := range nodes {
mmx[n.Name] = NodeMetrics{
opts := metav1.ListOptions{} AvailCPU: n.Status.Allocatable.Cpu().MilliValue(),
nn, err := conn.dialOrDie().CoreV1().Nodes().List(opts) AvailMEM: asMi(n.Status.Allocatable.Memory().Value()),
if err != nil { TotalCPU: n.Status.Capacity.Cpu().MilliValue(),
log.Warn().Msgf("%s", err) TotalMEM: asMi(n.Status.Capacity.Memory().Value()),
return mx, err
}
nods := make([]string, len(nn.Items))
var maxCPU, maxMem float64
for i, n := range nn.Items {
nods[i] = n.Name
c := n.Status.Allocatable["cpu"]
maxCPU += float64(c.MilliValue())
m := n.Status.Allocatable["memory"]
maxMem += float64(m.Value() / (1024 * 1024))
}
mm, err := m.getNodeMetrics()
if err != nil {
log.Warn().Msgf("%s", err)
return mx, err
}
var cpu, mem float64
for _, n := range nods {
for _, m := range mm.Items {
if m.Name == n {
cpu += float64(m.Usage.Cpu().MilliValue())
mem += float64(m.Usage.Memory().Value() / (1024 * 1024))
}
}
}
mx = Metric{
CPU: fmt.Sprintf("%0.f%%", math.Round((cpu/maxCPU)*100)),
Mem: fmt.Sprintf("%0.f%%", math.Round((mem/maxMem)*100)),
}
return mx, nil
}
// PodMetrics retrieves all pods metrics
func (m *MetricsServer) PodMetrics() (map[string]Metric, error) {
mx := map[string]Metric{}
mm, err := m.getPodMetrics()
if err != nil {
log.Warn().Msgf("%s", err)
return mx, err
}
for _, m := range mm.Items {
var cpu, mem int64
for _, c := range m.Containers {
cpu += c.Usage.Cpu().MilliValue()
mem += c.Usage.Memory().Value() / (1024 * 1024)
}
pa := path.Join(m.Namespace, m.Name)
mx[pa] = Metric{CPU: fmt.Sprintf("%dm", cpu), Mem: fmt.Sprintf("%dMi", mem)}
}
return mx, nil
}
// PerNodeMetrics retrieves all nodes metrics
func (m *MetricsServer) PerNodeMetrics(nn []v1.Node) (map[string]Metric, error) {
mx := map[string]Metric{}
mm, err := m.getNodeMetrics()
if err != nil {
log.Warn().Msgf("%s", err)
return mx, err
}
for _, n := range nn {
acpu := n.Status.Allocatable["cpu"]
amem := n.Status.Allocatable["memory"]
var cpu, mem int64
for _, m := range mm.Items {
if m.Name == n.Name {
cpu += m.Usage.Cpu().MilliValue()
mem += m.Usage.Memory().Value() / (1024 * 1024)
}
}
mx[n.Name] = Metric{
CPU: fmt.Sprintf("%dm", cpu),
Mem: fmt.Sprintf("%dMi", mem),
AvailCPU: fmt.Sprintf("%dm", acpu.MilliValue()),
AvailMem: fmt.Sprintf("%dMi", amem.Value()/(1024*1024)),
} }
} }
return mx, nil for _, c := range metrics {
if mx, ok := mmx[c.Name]; ok {
mx.CurrentCPU = c.Usage.Cpu().MilliValue()
mx.CurrentMEM = asMi(c.Usage.Memory().Value())
mmx[c.Name] = mx
}
}
} }
func (m *MetricsServer) getPodMetrics() (*metricsapi.PodMetricsList, error) { // ClusterLoad retrieves all cluster nodes metrics.
if conn.hasMetricsServer() { func (m *MetricsServer) ClusterLoad(nodes []v1.Node, metrics []mv1beta1.NodeMetrics) ClusterMetrics {
return m.podMetricsViaService() nodeMetrics := make(NodesMetrics, len(nodes))
for _, n := range nodes {
nodeMetrics[n.Name] = NodeMetrics{
AvailCPU: n.Status.Allocatable.Cpu().MilliValue(),
AvailMEM: asMi(n.Status.Allocatable.Memory().Value()),
TotalCPU: n.Status.Capacity.Cpu().MilliValue(),
TotalMEM: asMi(n.Status.Capacity.Memory().Value()),
}
} }
var mx *metricsapi.PodMetricsList for _, mx := range metrics {
conn, err := conn.heapsterDial() if m, ok := nodeMetrics[mx.Name]; ok {
if err != nil { m.CurrentCPU = mx.Usage.Cpu().MilliValue()
log.Warn().Msgf("%s", err) m.CurrentMEM = asMi(mx.Usage.Memory().Value())
return mx, err nodeMetrics[mx.Name] = m
}
} }
return conn.GetPodMetrics("", "", true, labels.Everything()) var cpu, tcpu, mem, tmem float64
for _, mx := range nodeMetrics {
cpu += float64(mx.CurrentCPU)
tcpu += float64(mx.TotalCPU)
mem += mx.CurrentMEM
tmem += mx.TotalMEM
}
return ClusterMetrics{PercCPU: toPerc(cpu, tcpu), PercMEM: toPerc(mem, tmem)}
} }
func (m *MetricsServer) getNodeMetrics() (*metricsapi.NodeMetricsList, error) { // // HasMetrics check if cluster has a metrics server.
if conn.hasMetricsServer() { // func (m *MetricsServer) HasMetrics() bool {
return m.nodeMetricsViaService() // return m.HasMetrics()
} // }
var mx *metricsapi.NodeMetricsList // FetchNodesMetrics return all metrics for pods in a given namespace.
conn, err := conn.heapsterDial() func (m *MetricsServer) FetchNodesMetrics() ([]mv1beta1.NodeMetrics, error) {
client, err := m.MXDial()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err)
return mx, err
}
return conn.GetNodeMetrics("", labels.Everything().String())
}
func (*MetricsServer) nodeMetricsViaService() (*metricsapi.NodeMetricsList, error) {
var mx *metricsapi.NodeMetricsList
clt, err := conn.mxsDial()
if err != nil {
log.Warn().Msgf("%s", err)
return mx, err
}
selector := labels.Everything()
var versionedMetrics *metricsV1beta1api.NodeMetricsList
mc := clt.Metrics()
nm := mc.NodeMetricses()
versionedMetrics, err = nm.List(metav1.ListOptions{LabelSelector: selector.String()})
if err != nil {
log.Warn().Msgf("%s", err)
return nil, err return nil, err
} }
metrics := &metricsapi.NodeMetricsList{} list, err := client.Metrics().NodeMetricses().List(metav1.ListOptions{})
err = metricsV1beta1api.Convert_v1beta1_NodeMetricsList_To_metrics_NodeMetricsList(versionedMetrics, metrics, nil)
if err != nil { if err != nil {
log.Warn().Msgf("%s", err)
return nil, err return nil, err
} }
return list.Items, nil
return metrics, nil
} }
func (*MetricsServer) podMetricsViaService() (*metricsapi.PodMetricsList, error) { // FetchPodsMetrics return all metrics for pods in a given namespace.
var mx *metricsapi.PodMetricsList func (m *MetricsServer) FetchPodsMetrics(ns string) ([]mv1beta1.PodMetrics, error) {
client, err := m.MXDial()
clt, err := conn.mxsDial()
if err != nil { if err != nil {
log.Warn().Msgf("%s", err)
return mx, err
}
selector := labels.Everything()
var versionedMetrics *metricsV1beta1api.PodMetricsList
mc := clt.Metrics()
nm := mc.PodMetricses("")
versionedMetrics, err = nm.List(metav1.ListOptions{LabelSelector: selector.String()})
if err != nil {
log.Warn().Msgf("%s", err)
return nil, err return nil, err
} }
metrics := &metricsapi.PodMetricsList{} list, err := client.Metrics().PodMetricses(ns).List(metav1.ListOptions{})
err = metricsV1beta1api.Convert_v1beta1_PodMetricsList_To_metrics_PodMetricsList(versionedMetrics, metrics, nil)
if err != nil { if err != nil {
log.Warn().Msgf("%s", err)
return nil, err return nil, err
} }
return list.Items, nil
return metrics, nil }
// PodsMetrics retrieves metrics for all pods in a given namespace.
func (m *MetricsServer) PodsMetrics(pods []mv1beta1.PodMetrics, mmx PodsMetrics) {
// Compute all pod's containers metrics.
for _, p := range pods {
var mx PodMetrics
for _, c := range p.Containers {
mx.CurrentCPU += c.Usage.Cpu().MilliValue()
mx.CurrentMEM += asMi(c.Usage.Memory().Value())
}
mmx[p.Namespace+"/"+p.Name] = mx
}
} }

View File

@ -0,0 +1,196 @@
package k8s
import (
"testing"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
v1beta1 "k8s.io/metrics/pkg/apis/metrics/v1beta1"
)
func TestPodsMetrics(t *testing.T) {
m := NewMetricsServer(nil)
metrics := v1beta1.PodMetricsList{
Items: []v1beta1.PodMetrics{
makeMxPod("p1", "1", "4Gi"),
makeMxPod("p2", "50m", "1Mi"),
},
}
mmx := make(PodsMetrics)
m.PodsMetrics(metrics.Items, mmx)
assert.Equal(t, 2, len(mmx))
mx, ok := mmx["default/p1"]
assert.True(t, ok)
assert.Equal(t, int64(3000), mx.CurrentCPU)
assert.Equal(t, float64(12288), mx.CurrentMEM)
}
func BenchmarkPodsMetrics(b *testing.B) {
m := NewMetricsServer(nil)
metrics := v1beta1.PodMetricsList{
Items: []v1beta1.PodMetrics{
makeMxPod("p1", "1", "4Gi"),
makeMxPod("p2", "50m", "1Mi"),
makeMxPod("p3", "50m", "1Mi"),
},
}
mmx := make(PodsMetrics, 3)
b.ResetTimer()
b.ReportAllocs()
for n := 0; n < b.N; n++ {
m.PodsMetrics(metrics.Items, mmx)
}
}
func TestNodesMetrics(t *testing.T) {
m := NewMetricsServer(nil)
nodes := v1.NodeList{
Items: []v1.Node{
makeNode("n1", "32", "128Gi", "50m", "2Mi"),
makeNode("n2", "8", "4Gi", "50m", "2Mi"),
},
}
metrics := v1beta1.NodeMetricsList{
Items: []v1beta1.NodeMetrics{
makeMxNode("n1", "10", "8Gi"),
makeMxNode("n2", "50m", "1Mi"),
},
}
mmx := make(NodesMetrics)
m.NodesMetrics(nodes.Items, metrics.Items, mmx)
assert.Equal(t, 2, len(mmx))
mx, ok := mmx["n1"]
assert.True(t, ok)
assert.Equal(t, int64(32000), mx.TotalCPU)
assert.Equal(t, float64(131072), mx.TotalMEM)
assert.Equal(t, int64(50), mx.AvailCPU)
assert.Equal(t, float64(2), mx.AvailMEM)
assert.Equal(t, int64(10000), mx.CurrentCPU)
assert.Equal(t, float64(8192), mx.CurrentMEM)
}
func BenchmarkNodesMetrics(b *testing.B) {
nodes := v1.NodeList{
Items: []v1.Node{
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
},
}
metrics := v1beta1.NodeMetricsList{
Items: []v1beta1.NodeMetrics{
makeMxNode("n1", "50m", "1Mi"),
makeMxNode("n2", "50m", "1Mi"),
},
}
m := NewMetricsServer(nil)
mmx := make(NodesMetrics)
b.ResetTimer()
b.ReportAllocs()
for n := 0; n < b.N; n++ {
m.NodesMetrics(nodes.Items, metrics.Items, mmx)
}
}
func TestClusterLoad(t *testing.T) {
m := NewMetricsServer(nil)
nodes := v1.NodeList{
Items: []v1.Node{
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
},
}
metrics := v1beta1.NodeMetricsList{
Items: []v1beta1.NodeMetrics{
makeMxNode("n1", "50m", "1Mi"),
makeMxNode("n2", "50m", "1Mi"),
},
}
mx := m.ClusterLoad(nodes.Items, metrics.Items)
assert.Equal(t, 50.0, mx.PercCPU)
assert.Equal(t, 25.0, mx.PercMEM)
}
func BenchmarkClusterLoad(b *testing.B) {
nodes := v1.NodeList{
Items: []v1.Node{
makeNode("n1", "100m", "4Mi", "50m", "2Mi"),
makeNode("n2", "100m", "4Mi", "50m", "2Mi"),
},
}
metrics := v1beta1.NodeMetricsList{
Items: []v1beta1.NodeMetrics{
makeMxNode("n1", "50m", "1Mi"),
makeMxNode("n2", "50m", "1Mi"),
},
}
m := NewMetricsServer(nil)
b.ResetTimer()
b.ReportAllocs()
for n := 0; n < b.N; n++ {
m.ClusterLoad(nodes.Items, metrics.Items)
}
}
func makeMxPod(name, cpu, mem string) v1beta1.PodMetrics {
return v1beta1.PodMetrics{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "default",
},
Containers: []v1beta1.ContainerMetrics{
{Usage: makeRes(cpu, mem)},
{Usage: makeRes(cpu, mem)},
{Usage: makeRes(cpu, mem)},
},
}
}
func makeNode(name, tcpu, tmem, acpu, amem string) v1.Node {
return v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Status: v1.NodeStatus{
Capacity: makeRes(tcpu, tmem),
Allocatable: makeRes(acpu, amem),
},
}
}
func makeMxNode(name, cpu, mem string) v1beta1.NodeMetrics {
return v1beta1.NodeMetrics{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Usage: makeRes(cpu, mem),
}
}
func makeRes(c, m string) v1.ResourceList {
cpu, _ := resource.ParseQuantity(c)
mem, _ := resource.ParseQuantity(m)
return v1.ResourceList{
v1.ResourceCPU: cpu,
v1.ResourceMemory: mem,
}
}

View File

@ -5,28 +5,26 @@ import (
) )
// Node represents a Kubernetes node. // Node represents a Kubernetes node.
type Node struct{} type Node struct {
Connection
}
// NewNode returns a new Node. // NewNode returns a new Node.
func NewNode() Res { func NewNode(c Connection) Cruder {
return &Node{} return &Node{c}
} }
// Get a node. // Get a node.
func (*Node) Get(_, n string) (interface{}, error) { func (n *Node) Get(_, name string) (interface{}, error) {
opts := metav1.GetOptions{} return n.DialOrDie().CoreV1().Nodes().Get(name, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Nodes().Get(n, opts)
} }
// List all nodes on the cluster. // List all nodes on the cluster.
func (*Node) List(_ string) (Collection, error) { func (n *Node) List(_ string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := n.DialOrDie().CoreV1().Nodes().List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Nodes().List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*Node) List(_ string) (Collection, error) {
} }
// Delete a node. // Delete a node.
func (*Node) Delete(_, n string) error { func (n *Node) Delete(_, name string) error {
opts := metav1.DeleteOptions{} return n.DialOrDie().CoreV1().Nodes().Delete(name, nil)
return conn.dialOrDie().CoreV1().Nodes().Delete(n, &opts)
} }

View File

@ -5,29 +5,26 @@ import (
) )
// Namespace represents a Kubernetes namespace. // Namespace represents a Kubernetes namespace.
type Namespace struct{} type Namespace struct {
Connection
}
// NewNamespace returns a new Namespace. // NewNamespace returns a new Namespace.
func NewNamespace() Res { func NewNamespace(c Connection) Cruder {
return &Namespace{} return &Namespace{c}
} }
// Get a active namespace. // Get a active namespace.
func (*Namespace) Get(_, n string) (interface{}, error) { func (n *Namespace) Get(_, name string) (interface{}, error) {
opts := metav1.GetOptions{} return n.DialOrDie().CoreV1().Namespaces().Get(name, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Namespaces().Get(n, opts)
} }
// List all active namespaces on the cluster. // List all active namespaces on the cluster.
func (*Namespace) List(_ string) (Collection, error) { func (n *Namespace) List(_ string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := n.DialOrDie().CoreV1().Namespaces().List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Namespaces().List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -37,8 +34,6 @@ func (*Namespace) List(_ string) (Collection, error) {
} }
// Delete a namespace. // Delete a namespace.
func (*Namespace) Delete(_, n string) error { func (n *Namespace) Delete(_, name string) error {
opts := metav1.DeleteOptions{} return n.DialOrDie().CoreV1().Namespaces().Delete(name, nil)
return conn.dialOrDie().CoreV1().Namespaces().Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// PodDisruptionBudget represents a PodDisruptionBudget Kubernetes resource. // PodDisruptionBudget represents a PodDisruptionBudget Kubernetes resource.
type PodDisruptionBudget struct{} type PodDisruptionBudget struct {
Connection
}
// NewPodDisruptionBudget returns a new PodDisruptionBudget. // NewPodDisruptionBudget returns a new PodDisruptionBudget.
func NewPodDisruptionBudget() Res { func NewPodDisruptionBudget(c Connection) Cruder {
return &PodDisruptionBudget{} return &PodDisruptionBudget{c}
} }
// Get a pdb. // Get a pdb.
func (*PodDisruptionBudget) Get(ns, n string) (interface{}, error) { func (p *PodDisruptionBudget) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).Get(n, opts)
} }
// List all pdbs in a given namespace. // List all pdbs in a given namespace.
func (*PodDisruptionBudget) List(ns string) (Collection, error) { func (p *PodDisruptionBudget) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*PodDisruptionBudget) List(ns string) (Collection, error) {
} }
// Delete a pdb. // Delete a pdb.
func (*PodDisruptionBudget) Delete(ns, n string) error { func (p *PodDisruptionBudget) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).Delete(n, nil)
return conn.dialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).Delete(n, &opts)
} }

View File

@ -8,38 +8,27 @@ import (
const defaultKillGrace int64 = 5 const defaultKillGrace int64 = 5
type ( // Pod represents a Kubernetes resource.
// Loggable represents a K8s resource that has containers and can be logged. type Pod struct {
Loggable interface { Connection
Res }
Containers(ns, n string, includeInit bool) ([]string, error)
Logs(ns, n, co string, lines int64, previous bool) *restclient.Request
}
// Pod represents a Kubernetes resource.
Pod struct{}
)
// NewPod returns a new Pod. // NewPod returns a new Pod.
func NewPod() Res { func NewPod(c Connection) Cruder {
return &Pod{} return &Pod{c}
} }
// Get a pod. // Get a pod.
func (*Pod) Get(ns, n string) (interface{}, error) { func (p *Pod) Get(ns, name string) (interface{}, error) {
opts := metav1.GetOptions{} return p.DialOrDie().CoreV1().Pods(ns).Get(name, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Pods(ns).Get(n, opts)
} }
// List all pods in a given namespace. // List all pods in a given namespace.
func (*Pod) List(ns string) (Collection, error) { func (p *Pod) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := p.DialOrDie().CoreV1().Pods(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Pods(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -49,51 +38,40 @@ func (*Pod) List(ns string) (Collection, error) {
} }
// Delete a pod. // Delete a pod.
func (*Pod) Delete(ns, n string) error { func (p *Pod) Delete(ns, n string) error {
var grace = defaultKillGrace // Make pods die faster?
opts := metav1.DeleteOptions{ grace := defaultKillGrace
return p.DialOrDie().CoreV1().Pods(ns).Delete(n, &metav1.DeleteOptions{
GracePeriodSeconds: &grace, GracePeriodSeconds: &grace,
} })
return conn.dialOrDie().CoreV1().Pods(ns).Delete(n, &opts)
} }
// Containers returns all container names on pod // Containers returns all container names on pod
func (*Pod) Containers(ns, n string, includeInit bool) ([]string, error) { func (p *Pod) Containers(ns, n string, includeInit bool) ([]string, error) {
opts := metav1.GetOptions{} po, err := p.DialOrDie().CoreV1().Pods(ns).Get(n, metav1.GetOptions{})
cc := []string{}
po, err := conn.dialOrDie().CoreV1().Pods(ns).Get(n, opts)
if err != nil { if err != nil {
return cc, err return nil, err
} }
for _, c := range po.Spec.Containers { cc := []string{}
cc = append(cc, c.Name)
}
if includeInit { if includeInit {
for _, c := range po.Spec.InitContainers { for _, c := range po.Spec.InitContainers {
cc = append(cc, c.Name) cc = append(cc, c.Name)
} }
} }
for _, c := range po.Spec.Containers {
cc = append(cc, c.Name)
}
return cc, nil return cc, nil
} }
// Logs fetch container logs for a given pod and container. // Logs fetch container logs for a given pod and container.
func (*Pod) Logs(ns, n, co string, lines int64, prev bool) *restclient.Request { func (p *Pod) Logs(ns, n, co string, lines int64, prev bool) *restclient.Request {
opts := &v1.PodLogOptions{ return p.DialOrDie().CoreV1().Pods(ns).GetLogs(n, &v1.PodLogOptions{
Container: co, Container: co,
Follow: true, Follow: true,
TailLines: &lines, TailLines: &lines,
Previous: prev, Previous: prev,
} })
return conn.dialOrDie().CoreV1().Pods(ns).GetLogs(n, opts)
}
// Events retrieved pod's events.
func (*Pod) Events(ns, n string) (*v1.EventList, error) {
e := conn.dialOrDie().Core().Events(ns)
sel := e.GetFieldSelector(&n, &ns, nil, nil)
opts := metav1.ListOptions{FieldSelector: sel.String()}
ee, err := e.List(opts)
return ee, err
} }

View File

@ -5,26 +5,25 @@ import (
) )
// PV represents a Kubernetes PersistentVolume. // PV represents a Kubernetes PersistentVolume.
type PV struct{} type PV struct {
Connection
}
// NewPV returns a new PV. // NewPV returns a new PV.
func NewPV() Res { func NewPV(c Connection) Cruder {
return &PV{} return &PV{c}
} }
// Get a PV. // Get a PV.
func (*PV) Get(_, n string) (interface{}, error) { func (p *PV) Get(_, n string) (interface{}, error) {
opts := metav1.GetOptions{} return p.DialOrDie().CoreV1().PersistentVolumes().Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().PersistentVolumes().Get(n, opts)
} }
// List all PVs in a given namespace. // List all PVs in a given namespace.
func (*PV) List(_ string) (Collection, error) { func (p *PV) List(_ string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := p.DialOrDie().CoreV1().PersistentVolumes().List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().PersistentVolumes().List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
@ -36,7 +35,6 @@ func (*PV) List(_ string) (Collection, error) {
} }
// Delete a PV. // Delete a PV.
func (*PV) Delete(_, n string) error { func (p *PV) Delete(_, n string) error {
opts := metav1.DeleteOptions{} return p.DialOrDie().CoreV1().PersistentVolumes().Delete(n, nil)
return conn.dialOrDie().CoreV1().PersistentVolumes().Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// PVC represents a Kubernetes service. // PVC represents a Kubernetes service.
type PVC struct{} type PVC struct {
Connection
}
// NewPVC returns a new PVC. // NewPVC returns a new PVC.
func NewPVC() Res { func NewPVC(c Connection) Cruder {
return &PVC{} return &PVC{c}
} }
// Get a PVC. // Get a PVC.
func (*PVC) Get(ns, n string) (interface{}, error) { func (p *PVC) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().PersistentVolumeClaims(ns).Get(n, opts)
} }
// List all PVCs in a given namespace. // List all PVCs in a given namespace.
func (*PVC) List(ns string) (Collection, error) { func (p *PVC) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().PersistentVolumeClaims(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*PVC) List(ns string) (Collection, error) {
} }
// Delete a PVC. // Delete a PVC.
func (*PVC) Delete(ns, n string) error { func (p *PVC) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().PersistentVolumeClaims(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// ReplicationController represents a Kubernetes service. // ReplicationController represents a Kubernetes service.
type ReplicationController struct{} type ReplicationController struct {
Connection
}
// NewReplicationController returns a new ReplicationController. // NewReplicationController returns a new ReplicationController.
func NewReplicationController() Res { func NewReplicationController(c Connection) Cruder {
return &ReplicationController{} return &ReplicationController{c}
} }
// Get a RC. // Get a RC.
func (*ReplicationController) Get(ns, n string) (interface{}, error) { func (r *ReplicationController) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return r.DialOrDie().Core().ReplicationControllers(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().Core().ReplicationControllers(ns).Get(n, opts)
} }
// List all RCs in a given namespace. // List all RCs in a given namespace.
func (*ReplicationController) List(ns string) (Collection, error) { func (r *ReplicationController) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := r.DialOrDie().Core().ReplicationControllers(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().Core().ReplicationControllers(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*ReplicationController) List(ns string) (Collection, error) {
} }
// Delete a RC. // Delete a RC.
func (*ReplicationController) Delete(ns, n string) error { func (r *ReplicationController) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return r.DialOrDie().Core().ReplicationControllers(ns).Delete(n, nil)
return conn.dialOrDie().Core().ReplicationControllers(ns).Delete(n, &opts)
} }

View File

@ -3,7 +3,6 @@ package k8s
import ( import (
"fmt" "fmt"
"github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -15,12 +14,14 @@ import (
// Resource represents a Kubernetes Resource // Resource represents a Kubernetes Resource
type Resource struct { type Resource struct {
Connection
group, version, name string group, version, name string
} }
// NewResource returns a new Resource. // NewResource returns a new Resource.
func NewResource(group, version, name string) Res { func NewResource(c Connection, group, version, name string) Cruder {
return &Resource{group: group, version: version, name: name} return &Resource{Connection: c, group: group, version: version, name: name}
} }
// GetInfo returns info about apigroup. // GetInfo returns info about apigroup.
@ -34,48 +35,82 @@ func (r *Resource) base() dynamic.NamespaceableResourceInterface {
Version: r.version, Version: r.version,
Resource: r.name, Resource: r.name,
} }
return conn.dynDialOrDie().Resource(g) return r.DynDialOrDie().Resource(g)
} }
// Get a Resource. // Get a Resource.
func (r *Resource) Get(ns, n string) (interface{}, error) { func (r *Resource) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return r.base().Namespace(ns).Get(n, metav1.GetOptions{})
return r.base().Namespace(ns).Get(n, opts)
} }
// List all Resources in a given namespace. // List all Resources in a given namespace.
func (r *Resource) List(ns string) (Collection, error) { func (r *Resource) List(ns string) (Collection, error) {
obj, err := r.listAll(ns, r.name) obj, err := r.listAll(ns, r.name)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
return Collection{obj.(*metav1beta1.Table)}, nil return Collection{obj.(*metav1beta1.Table)}, nil
} }
// Delete a Resource. // Delete a Resource.
func (r *Resource) Delete(ns, n string) error { func (r *Resource) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return r.base().Namespace(ns).Delete(n, nil)
return r.base().Namespace(ns).Delete(n, &opts)
} }
// ----------------------------------------------------------------------------
// Helpers... // Helpers...
func (r *Resource) getClient() *rest.RESTClient { const gvFmt = "application/json;as=Table;v=%s;g=%s, application/json"
gv := schema.GroupVersion{Group: r.group, Version: r.version}
codecs, _ := r.codecs() func (r *Resource) listAll(ns, n string) (runtime.Object, error) {
crConfig := *conn.restConfigOrDie() a := fmt.Sprintf(gvFmt, metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)
crConfig.GroupVersion = &gv _, codec := r.codecs()
c, err := r.getClient()
if err != nil {
return nil, err
}
return c.Get().
SetHeader("Accept", a).
Namespace(ns).
Resource(n).
VersionedParams(&metav1beta1.TableOptions{}, codec).
Do().Get()
}
func (r *Resource) getOne(ns, n string) (runtime.Object, error) {
a := fmt.Sprintf(gvFmt, metav1beta1.SchemeGroupVersion.Version, metav1beta1.GroupName)
_, codec := r.codecs()
c, err := r.getClient()
if err != nil {
return nil, err
}
return c.Get().
SetHeader("Accept", a).
Namespace(ns).
Resource(n).
VersionedParams(&metav1beta1.TableOptions{}, codec).
Do().Get()
}
func (r *Resource) getClient() (*rest.RESTClient, error) {
crConfig := r.RestConfigOrDie()
crConfig.GroupVersion = &schema.GroupVersion{Group: r.group, Version: r.version}
crConfig.APIPath = "/apis" crConfig.APIPath = "/apis"
if len(r.group) == 0 { if len(r.group) == 0 {
crConfig.APIPath = "/api" crConfig.APIPath = "/api"
} }
codecs, _ := r.codecs()
crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs} crConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: codecs}
crRestClient, err := rest.RESTClientFor(&crConfig)
crRestClient, err := rest.RESTClientFor(crConfig)
if err != nil { if err != nil {
log.Fatal().Err(err) return nil, err
} }
return crRestClient return crRestClient, nil
} }
func (r *Resource) codecs() (serializer.CodecFactory, runtime.ParameterCodec) { func (r *Resource) codecs() (serializer.CodecFactory, runtime.ParameterCodec) {
@ -84,36 +119,6 @@ func (r *Resource) codecs() (serializer.CodecFactory, runtime.ParameterCodec) {
metav1.AddToGroupVersion(scheme, gv) metav1.AddToGroupVersion(scheme, gv)
scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{}) scheme.AddKnownTypes(gv, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{}) scheme.AddKnownTypes(metav1beta1.SchemeGroupVersion, &metav1beta1.Table{}, &metav1beta1.TableOptions{})
codecs := serializer.NewCodecFactory(scheme)
return codecs, runtime.NewParameterCodec(scheme) return serializer.NewCodecFactory(scheme), runtime.NewParameterCodec(scheme)
}
func (r *Resource) listAll(ns, n string) (runtime.Object, error) {
group := metav1beta1.GroupName
version := metav1beta1.SchemeGroupVersion.Version
a := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
_, codec := r.codecs()
return r.getClient().Get().
SetHeader("Accept", a).
Namespace(ns).
Resource(n).
VersionedParams(&metav1beta1.TableOptions{}, codec).
Do().
Get()
}
func (r *Resource) getOne(ns, n string) (runtime.Object, error) {
group := metav1beta1.GroupName
version := metav1beta1.SchemeGroupVersion.Version
a := fmt.Sprintf("application/json;as=Table;v=%s;g=%s, application/json", version, group)
_, codec := r.codecs()
return r.getClient().Get().
SetHeader("Accept", a).
Namespace(ns).
Resource(n).
VersionedParams(&metav1beta1.TableOptions{}, codec).
Do().
Get()
} }

View File

@ -6,28 +6,26 @@ import (
) )
// Role represents a Kubernetes Role. // Role represents a Kubernetes Role.
type Role struct{} type Role struct {
Connection
}
// NewRole returns a new Role. // NewRole returns a new Role.
func NewRole() Res { func NewRole(c Connection) Cruder {
return &Role{} return &Role{c}
} }
// Get a Role. // Get a Role.
func (*Role) Get(ns, n string) (interface{}, error) { func (r *Role) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return r.DialOrDie().RbacV1().Roles(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().RbacV1().Roles(ns).Get(n, opts)
} }
// List all Roles in a given namespace. // List all Roles in a given namespace.
func (*Role) List(ns string) (Collection, error) { func (r *Role) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := r.DialOrDie().RbacV1().Roles(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().RbacV1().Roles(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -37,7 +35,6 @@ func (*Role) List(ns string) (Collection, error) {
} }
// Delete a Role. // Delete a Role.
func (*Role) Delete(ns, n string) error { func (r *Role) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return r.DialOrDie().RbacV1().Roles(ns).Delete(n, nil)
return conn.dialOrDie().RbacV1().Roles(ns).Delete(n, &opts)
} }

View File

@ -3,28 +3,26 @@ package k8s
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
// RoleBinding represents a Kubernetes RoleBinding. // RoleBinding represents a Kubernetes RoleBinding.
type RoleBinding struct{} type RoleBinding struct {
Connection
}
// NewRoleBinding returns a new RoleBinding. // NewRoleBinding returns a new RoleBinding.
func NewRoleBinding() Res { func NewRoleBinding(c Connection) Cruder {
return &RoleBinding{} return &RoleBinding{c}
} }
// Get a RoleBinding. // Get a RoleBinding.
func (*RoleBinding) Get(ns, n string) (interface{}, error) { func (r *RoleBinding) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return r.DialOrDie().RbacV1().RoleBindings(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().RbacV1().RoleBindings(ns).Get(n, opts)
} }
// List all RoleBindings in a given namespace. // List all RoleBindings in a given namespace.
func (*RoleBinding) List(ns string) (Collection, error) { func (r *RoleBinding) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := r.DialOrDie().RbacV1().RoleBindings(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().RbacV1().RoleBindings(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -34,7 +32,6 @@ func (*RoleBinding) List(ns string) (Collection, error) {
} }
// Delete a RoleBinding. // Delete a RoleBinding.
func (*RoleBinding) Delete(ns, n string) error { func (r *RoleBinding) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return r.DialOrDie().RbacV1().RoleBindings(ns).Delete(n, nil)
return conn.dialOrDie().RbacV1().RoleBindings(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// ReplicaSet represents a Kubernetes ReplicaSet. // ReplicaSet represents a Kubernetes ReplicaSet.
type ReplicaSet struct{} type ReplicaSet struct {
Connection
}
// NewReplicaSet returns a new ReplicaSet. // NewReplicaSet returns a new ReplicaSet.
func NewReplicaSet() Res { func NewReplicaSet(c Connection) Cruder {
return &ReplicaSet{} return &ReplicaSet{c}
} }
// Get a ReplicaSet. // Get a ReplicaSet.
func (*ReplicaSet) Get(ns, n string) (interface{}, error) { func (r *ReplicaSet) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return r.DialOrDie().Apps().ReplicaSets(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().Apps().ReplicaSets(ns).Get(n, opts)
} }
// List all ReplicaSets in a given namespace. // List all ReplicaSets in a given namespace.
func (*ReplicaSet) List(ns string) (Collection, error) { func (r *ReplicaSet) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := r.DialOrDie().Apps().ReplicaSets(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().Apps().ReplicaSets(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*ReplicaSet) List(ns string) (Collection, error) {
} }
// Delete a ReplicaSet. // Delete a ReplicaSet.
func (*ReplicaSet) Delete(ns, n string) error { func (r *ReplicaSet) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return r.DialOrDie().Apps().ReplicaSets(ns).Delete(n, nil)
return conn.dialOrDie().Apps().ReplicaSets(ns).Delete(n, &opts)
} }

View File

@ -5,32 +5,26 @@ import (
) )
// ServiceAccount manages a Kubernetes ServiceAccount. // ServiceAccount manages a Kubernetes ServiceAccount.
type ServiceAccount struct{} type ServiceAccount struct {
Connection
}
// NewServiceAccount instantiates a new ServiceAccount. // NewServiceAccount instantiates a new ServiceAccount.
func NewServiceAccount() Res { func NewServiceAccount(c Connection) Cruder {
return &ServiceAccount{} return &ServiceAccount{c}
} }
// Get a ServiceAccount. // Get a ServiceAccount.
func (*ServiceAccount) Get(ns, n string) (interface{}, error) { func (s *ServiceAccount) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return s.DialOrDie().CoreV1().ServiceAccounts(ns).Get(n, metav1.GetOptions{})
o, err := conn.dialOrDie().CoreV1().ServiceAccounts(ns).Get(n, opts)
if err != nil {
return o, err
}
return o, nil
} }
// List all ServiceAccounts in a given namespace. // List all ServiceAccounts in a given namespace.
func (*ServiceAccount) List(ns string) (Collection, error) { func (s *ServiceAccount) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := s.DialOrDie().CoreV1().ServiceAccounts(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().ServiceAccounts(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -40,7 +34,6 @@ func (*ServiceAccount) List(ns string) (Collection, error) {
} }
// Delete a ServiceAccount. // Delete a ServiceAccount.
func (*ServiceAccount) Delete(ns, n string) error { func (s *ServiceAccount) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return s.DialOrDie().CoreV1().ServiceAccounts(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().ServiceAccounts(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// Secret represents a Kubernetes Secret. // Secret represents a Kubernetes Secret.
type Secret struct{} type Secret struct {
Connection
}
// NewSecret returns a new Secret. // NewSecret returns a new Secret.
func NewSecret() Res { func NewSecret(c Connection) Cruder {
return &Secret{} return &Secret{c}
} }
// Get a Secret. // Get a Secret.
func (c *Secret) Get(ns, n string) (interface{}, error) { func (s *Secret) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return s.DialOrDie().CoreV1().Secrets(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Secrets(ns).Get(n, opts)
} }
// List all Secrets in a given namespace. // List all Secrets in a given namespace.
func (c *Secret) List(ns string) (Collection, error) { func (s *Secret) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := s.DialOrDie().CoreV1().Secrets(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Secrets(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (c *Secret) List(ns string) (Collection, error) {
} }
// Delete a Secret. // Delete a Secret.
func (c *Secret) Delete(ns, n string) error { func (s *Secret) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return s.DialOrDie().CoreV1().Secrets(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().Secrets(ns).Delete(n, &opts)
} }

View File

@ -5,41 +5,35 @@ import (
) )
// StatefulSet manages a Kubernetes StatefulSet. // StatefulSet manages a Kubernetes StatefulSet.
type StatefulSet struct{} type StatefulSet struct {
Connection
}
// NewStatefulSet instantiates a new StatefulSet. // NewStatefulSet instantiates a new StatefulSet.
func NewStatefulSet() Res { func NewStatefulSet(c Connection) Cruder {
return &StatefulSet{} return &StatefulSet{c}
} }
// Get a StatefulSet. // Get a StatefulSet.
func (*StatefulSet) Get(ns, n string) (interface{}, error) { func (s *StatefulSet) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return s.DialOrDie().AppsV1().StatefulSets(ns).Get(n, metav1.GetOptions{})
o, err := conn.dialOrDie().AppsV1().StatefulSets(ns).Get(n, opts)
if err != nil {
return o, err
}
return o, nil
} }
// List all StatefulSets in a given namespace. // List all StatefulSets in a given namespace.
func (*StatefulSet) List(ns string) (Collection, error) { func (s *StatefulSet) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := s.DialOrDie().AppsV1().StatefulSets(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().AppsV1().StatefulSets(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
} }
return cc, nil return cc, nil
} }
// Delete a StatefulSet. // Delete a StatefulSet.
func (*StatefulSet) Delete(ns, n string) error { func (s *StatefulSet) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return s.DialOrDie().AppsV1().StatefulSets(ns).Delete(n, nil)
return conn.dialOrDie().AppsV1().StatefulSets(ns).Delete(n, &opts)
} }

View File

@ -5,28 +5,26 @@ import (
) )
// Service represents a Kubernetes Service. // Service represents a Kubernetes Service.
type Service struct{} type Service struct {
Connection
}
// NewService returns a new Service. // NewService returns a new Service.
func NewService() Res { func NewService(c Connection) Cruder {
return &Service{} return &Service{c}
} }
// Get a service. // Get a service.
func (*Service) Get(ns, n string) (interface{}, error) { func (s *Service) Get(ns, n string) (interface{}, error) {
opts := metav1.GetOptions{} return s.DialOrDie().CoreV1().Services(ns).Get(n, metav1.GetOptions{})
return conn.dialOrDie().CoreV1().Services(ns).Get(n, opts)
} }
// List all Services in a given namespace. // List all Services in a given namespace.
func (*Service) List(ns string) (Collection, error) { func (s *Service) List(ns string) (Collection, error) {
opts := metav1.ListOptions{} rr, err := s.DialOrDie().CoreV1().Services(ns).List(metav1.ListOptions{})
rr, err := conn.dialOrDie().CoreV1().Services(ns).List(opts)
if err != nil { if err != nil {
return Collection{}, err return nil, err
} }
cc := make(Collection, len(rr.Items)) cc := make(Collection, len(rr.Items))
for i, r := range rr.Items { for i, r := range rr.Items {
cc[i] = r cc[i] = r
@ -36,7 +34,6 @@ func (*Service) List(ns string) (Collection, error) {
} }
// Delete a Service. // Delete a Service.
func (*Service) Delete(ns, n string) error { func (s *Service) Delete(ns, n string) error {
opts := metav1.DeleteOptions{} return s.DialOrDie().CoreV1().Services(ns).Delete(n, nil)
return conn.dialOrDie().CoreV1().Services(ns).Delete(n, &opts)
} }