refact and rename
parent
a89a594893
commit
bb42c39645
|
|
@ -0,0 +1,39 @@
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/k9s_small.png" align="right" width="200" height="auto"/>
|
||||||
|
|
||||||
|
# Release v0.4.9
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
Thank you to all that contributed with flushing out issues with K9s! I'll try to mark some of these issues as fixed. But if you don't mind grab the latest rev and see if we're happier with some of the fixes!
|
||||||
|
|
||||||
|
If you've filed an issue please help me verify and close.
|
||||||
|
|
||||||
|
Thank you so much for your support and awesome suggestions to make K9s better!!
|
||||||
|
|
||||||
|
Also if you dig this tool, please make some noise on social! [@kitesurfer](https://twitter.com/kitesurfer)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Change Logs
|
||||||
|
|
||||||
|
### Popeye
|
||||||
|
|
||||||
|
Turns out [Popeye](https://github.com/derailed/popeye) is in too much flux at present, thus I've decided to remove it from K9s for the time being.
|
||||||
|
|
||||||
|
### ContainerView
|
||||||
|
|
||||||
|
Added a container view to list all the containers available on a given pod. On a selected pod, you can now press `<enter>` to view all of it's associated containers. Once in container view pressing `<enter>` on a selected container, will show the container logs.
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Resolved Bugs
|
||||||
|
|
||||||
|
+ [Issue #163](https://github.com/derailed/k9s/issues/163)
|
||||||
|
+ [Issue #162](https://github.com/derailed/k9s/issues/162)
|
||||||
|
+ [Issue #39](https://github.com/derailed/k9s/issues/39)
|
||||||
|
+ [Issue #27](https://github.com/derailed/k9s/issues/27)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<img src="https://raw.githubusercontent.com/derailed/k9s/master/assets/imhotep_logo.png" width="32" height="auto"/> © 2019 Imhotep Software LLC. All materials licensed under [Apache v2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
1
go.mod
1
go.mod
|
|
@ -23,4 +23,5 @@ require (
|
||||||
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f
|
k8s.io/metrics v0.0.0-20181121073115-d8618695b08f
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190404181321-646549c5a231 // indirect
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190404181321-646549c5a231 // indirect
|
||||||
sigs.k8s.io/yaml v1.1.0
|
sigs.k8s.io/yaml v1.1.0
|
||||||
|
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787
|
||||||
)
|
)
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -373,3 +373,5 @@ sigs.k8s.io/structured-merge-diff v0.0.0-20190404181321-646549c5a231 h1:jn9MygT5
|
||||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190404181321-646549c5a231/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
sigs.k8s.io/structured-merge-diff v0.0.0-20190404181321-646549c5a231/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
|
||||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||||
|
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787 h1:O69FD9pJA4WUZlEwYatBEEkRWKQ5cKodWpdKTrCS/iQ=
|
||||||
|
vbom.ml/util v0.0.0-20180919145318-efcd4e0f9787/go.mod h1:so/NYdZXCz+E3ZpW0uAoCj6uzU2+8OWDFv/HxUSs7kI=
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ type (
|
||||||
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
|
||||||
|
SetFieldSelector(string)
|
||||||
|
SetLabelSelector(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection represents a Kubenetes apiserver connection.
|
// Connection represents a Kubenetes apiserver connection.
|
||||||
|
|
@ -78,8 +80,7 @@ func InitConnectionOrDie(config *Config, logger zerolog.Logger) *APIClient {
|
||||||
return &conn
|
return &conn
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidNamespaces returns a collection of valid namespaces.
|
// ValidNamespaces returns all available namespaces.
|
||||||
// Bozo!! filter active?
|
|
||||||
func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
func (a *APIClient) ValidNamespaces() ([]v1.Namespace, error) {
|
||||||
nn, err := a.DialOrDie().CoreV1().Namespaces().List(metav1.ListOptions{})
|
nn, err := a.DialOrDie().CoreV1().Namespaces().List(metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package k8s
|
||||||
|
|
||||||
|
type base struct {
|
||||||
|
fieldSelector string
|
||||||
|
labelSelector string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFieldSelector refines query results via selector.
|
||||||
|
func (b *base) SetFieldSelector(s string) {
|
||||||
|
b.fieldSelector = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabelSelector refines query results via labels.
|
||||||
|
func (b *base) SetLabelSelector(s string) {
|
||||||
|
b.labelSelector = s
|
||||||
|
}
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// ClusterRole represents a Kubernetes ClusterRole
|
// ClusterRole represents a Kubernetes ClusterRole
|
||||||
type ClusterRole struct {
|
type ClusterRole struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClusterRole returns a new ClusterRole.
|
// NewClusterRole returns a new ClusterRole.
|
||||||
func NewClusterRole(c Connection) Cruder {
|
func NewClusterRole(c Connection) *ClusterRole {
|
||||||
return &ClusterRole{c}
|
return &ClusterRole{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a cluster role.
|
// Get a cluster role.
|
||||||
|
|
@ -21,7 +22,11 @@ func (c *ClusterRole) Get(_, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all ClusterRoles on a cluster.
|
// List all ClusterRoles on a cluster.
|
||||||
func (c *ClusterRole) List(_ string) (Collection, error) {
|
func (c *ClusterRole) List(_ string) (Collection, error) {
|
||||||
rr, err := c.DialOrDie().RbacV1().ClusterRoles().List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: c.labelSelector,
|
||||||
|
FieldSelector: c.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := c.DialOrDie().RbacV1().ClusterRoles().List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// ClusterRoleBinding represents a Kubernetes ClusterRoleBinding
|
// ClusterRoleBinding represents a Kubernetes ClusterRoleBinding
|
||||||
type ClusterRoleBinding struct {
|
type ClusterRoleBinding struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClusterRoleBinding returns a new ClusterRoleBinding.
|
// NewClusterRoleBinding returns a new ClusterRoleBinding.
|
||||||
func NewClusterRoleBinding(c Connection) Cruder {
|
func NewClusterRoleBinding(c Connection) *ClusterRoleBinding {
|
||||||
return &ClusterRoleBinding{c}
|
return &ClusterRoleBinding{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a service.
|
// Get a service.
|
||||||
|
|
@ -21,7 +22,11 @@ func (c *ClusterRoleBinding) Get(_, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all ClusterRoleBindings on a cluster.
|
// List all ClusterRoleBindings on a cluster.
|
||||||
func (c *ClusterRoleBinding) List(_ string) (Collection, error) {
|
func (c *ClusterRoleBinding) List(_ string) (Collection, error) {
|
||||||
rr, err := c.DialOrDie().RbacV1().ClusterRoleBindings().List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: c.labelSelector,
|
||||||
|
FieldSelector: c.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := c.DialOrDie().RbacV1().ClusterRoleBindings().List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Collection{}, err
|
return Collection{}, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// ConfigMap represents a Kubernetes ConfigMap
|
// ConfigMap represents a Kubernetes ConfigMap
|
||||||
type ConfigMap struct {
|
type ConfigMap struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfigMap returns a new ConfigMap.
|
// NewConfigMap returns a new ConfigMap.
|
||||||
func NewConfigMap(c Connection) Cruder {
|
func NewConfigMap(c Connection) *ConfigMap {
|
||||||
return &ConfigMap{c}
|
return &ConfigMap{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a ConfigMap.
|
// Get a ConfigMap.
|
||||||
|
|
@ -21,7 +22,11 @@ func (c *ConfigMap) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// 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) {
|
||||||
rr, err := c.DialOrDie().CoreV1().ConfigMaps(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: c.labelSelector,
|
||||||
|
FieldSelector: c.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := c.DialOrDie().CoreV1().ConfigMaps(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,12 +32,13 @@ func (c *NamedContext) MustCurrentContextName() string {
|
||||||
|
|
||||||
// Context represents a Kubernetes Context.
|
// Context represents a Kubernetes Context.
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns a new Context.
|
// NewContext returns a new Context.
|
||||||
func NewContext(c Connection) Cruder {
|
func NewContext(c Connection) *Context {
|
||||||
return &Context{c}
|
return &Context{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Context.
|
// Get a Context.
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,29 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CRD represents a Kubernetes CRD
|
// CustomResourceDefinition represents a Kubernetes CustomResourceDefinition
|
||||||
type CRD struct {
|
type CustomResourceDefinition struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCRD returns a new CRD.
|
// NewCustomResourceDefinition returns a new CustomResourceDefinition.
|
||||||
func NewCRD(c Connection) Cruder {
|
func NewCustomResourceDefinition(c Connection) *CustomResourceDefinition {
|
||||||
return &CRD{c}
|
return &CustomResourceDefinition{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a CRD.
|
// Get a CustomResourceDefinition.
|
||||||
func (c *CRD) Get(_, n string) (interface{}, error) {
|
func (c *CustomResourceDefinition) Get(_, n string) (interface{}, error) {
|
||||||
return c.NSDialOrDie().Get(n, metav1.GetOptions{})
|
return c.NSDialOrDie().Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all CRDs in a given namespace.
|
// List all CustomResourceDefinitions in a given namespace.
|
||||||
func (c *CRD) List(string) (Collection, error) {
|
func (c *CustomResourceDefinition) List(string) (Collection, error) {
|
||||||
rr, err := c.NSDialOrDie().List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: c.labelSelector,
|
||||||
|
FieldSelector: c.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := c.NSDialOrDie().List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +38,7 @@ func (c *CRD) List(string) (Collection, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a CRD.
|
// Delete a CustomResourceDefinition.
|
||||||
func (c *CRD) Delete(_, n string) error {
|
func (c *CustomResourceDefinition) Delete(_, n string) error {
|
||||||
return c.NSDialOrDie().Delete(n, nil)
|
return c.NSDialOrDie().Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,13 @@ const maxJobNameSize = 42
|
||||||
|
|
||||||
// CronJob represents a Kubernetes CronJob.
|
// CronJob represents a Kubernetes CronJob.
|
||||||
type CronJob struct {
|
type CronJob struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCronJob returns a new CronJob.
|
// NewCronJob returns a new CronJob.
|
||||||
func NewCronJob(c Connection) Cruder {
|
func NewCronJob(c Connection) *CronJob {
|
||||||
return &CronJob{c}
|
return &CronJob{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a CronJob.
|
// Get a CronJob.
|
||||||
|
|
@ -26,7 +27,11 @@ func (c *CronJob) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// 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) {
|
||||||
rr, err := c.DialOrDie().BatchV1beta1().CronJobs(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: c.labelSelector,
|
||||||
|
FieldSelector: c.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := c.DialOrDie().BatchV1beta1().CronJobs(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Deployment represents a Kubernetes Deployment.
|
// Deployment represents a Kubernetes Deployment.
|
||||||
type Deployment struct {
|
type Deployment struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDeployment returns a new Deployment.
|
// NewDeployment returns a new Deployment.
|
||||||
func NewDeployment(c Connection) Cruder {
|
func NewDeployment(c Connection) *Deployment {
|
||||||
return &Deployment{c}
|
return &Deployment{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a deployment.
|
// Get a deployment.
|
||||||
|
|
@ -21,7 +22,11 @@ func (d *Deployment) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Deployments in a given namespace.
|
// List all Deployments in a given namespace.
|
||||||
func (d *Deployment) List(ns string) (Collection, error) {
|
func (d *Deployment) List(ns string) (Collection, error) {
|
||||||
rr, err := d.DialOrDie().Apps().Deployments(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: d.labelSelector,
|
||||||
|
FieldSelector: d.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := d.DialOrDie().Apps().Deployments(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// DaemonSet represents a Kubernetes DaemonSet
|
// DaemonSet represents a Kubernetes DaemonSet
|
||||||
type DaemonSet struct {
|
type DaemonSet struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDaemonSet returns a new DaemonSet.
|
// NewDaemonSet returns a new DaemonSet.
|
||||||
func NewDaemonSet(c Connection) Cruder {
|
func NewDaemonSet(c Connection) *DaemonSet {
|
||||||
return &DaemonSet{c}
|
return &DaemonSet{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a DaemonSet.
|
// Get a DaemonSet.
|
||||||
|
|
@ -21,7 +22,11 @@ func (d *DaemonSet) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all DaemonSets in a given namespace.
|
// List all DaemonSets in a given namespace.
|
||||||
func (d *DaemonSet) List(ns string) (Collection, error) {
|
func (d *DaemonSet) List(ns string) (Collection, error) {
|
||||||
rr, err := d.DialOrDie().ExtensionsV1beta1().DaemonSets(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: d.labelSelector,
|
||||||
|
FieldSelector: d.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := d.DialOrDie().ExtensionsV1beta1().DaemonSets(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Endpoints represents a Kubernetes Endpoints.
|
// Endpoints represents a Kubernetes Endpoints.
|
||||||
type Endpoints struct {
|
type Endpoints struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEndpoints returns a new Endpoints.
|
// NewEndpoints returns a new Endpoints.
|
||||||
func NewEndpoints(c Connection) Cruder {
|
func NewEndpoints(c Connection) *Endpoints {
|
||||||
return &Endpoints{c}
|
return &Endpoints{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Endpoint.
|
// Get a Endpoint.
|
||||||
|
|
@ -21,7 +22,11 @@ func (e *Endpoints) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Endpoints in a given namespace.
|
// List all Endpoints in a given namespace.
|
||||||
func (e *Endpoints) List(ns string) (Collection, error) {
|
func (e *Endpoints) List(ns string) (Collection, error) {
|
||||||
rr, err := e.DialOrDie().CoreV1().Endpoints(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: e.labelSelector,
|
||||||
|
FieldSelector: e.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := e.DialOrDie().CoreV1().Endpoints(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Event represents a Kubernetes Event.
|
// Event represents a Kubernetes Event.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEvent returns a new Event.
|
// NewEvent returns a new Event.
|
||||||
func NewEvent(c Connection) Cruder {
|
func NewEvent(c Connection) *Event {
|
||||||
return &Event{c}
|
return &Event{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Event.
|
// Get a Event.
|
||||||
|
|
@ -21,7 +22,11 @@ func (e *Event) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Events in a given namespace.
|
// List all Events in a given namespace.
|
||||||
func (e *Event) List(ns string) (Collection, error) {
|
func (e *Event) List(ns string) (Collection, error) {
|
||||||
rr, err := e.DialOrDie().CoreV1().Events(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: e.labelSelector,
|
||||||
|
FieldSelector: e.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := e.DialOrDie().CoreV1().Events(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import (
|
||||||
|
|
||||||
const megaByte = 1024 * 1024
|
const megaByte = 1024 * 1024
|
||||||
|
|
||||||
func asMi(v int64) float64 {
|
// ToMB converts bytes to megabytes.
|
||||||
|
func ToMB(v int64) float64 {
|
||||||
return float64(v) / megaByte
|
return float64(v) / megaByte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ func TestToPerc(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAsMi(t *testing.T) {
|
func TestToMB(t *testing.T) {
|
||||||
uu := []struct {
|
uu := []struct {
|
||||||
v int64
|
v int64
|
||||||
e float64
|
e float64
|
||||||
|
|
@ -31,6 +31,6 @@ func TestAsMi(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range uu {
|
for _, u := range uu {
|
||||||
assert.Equal(t, u.e, asMi(u.v))
|
assert.Equal(t, u.e, ToMB(u.v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,29 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HPAV1 represents am HorizontalPodAutoscaler.
|
// HorizontalPodAutoscalerV1 represents am HorizontalPodAutoscaler.
|
||||||
type HPAV1 struct {
|
type HorizontalPodAutoscalerV1 struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV1 returns a new HPA.
|
// NewHorizontalPodAutoscalerV1 returns a new HorizontalPodAutoscaler.
|
||||||
func NewHPAV1(c Connection) Cruder {
|
func NewHorizontalPodAutoscalerV1(c Connection) *HorizontalPodAutoscalerV1 {
|
||||||
return &HPAV1{c}
|
return &HorizontalPodAutoscalerV1{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a HPA.
|
// Get a HorizontalPodAutoscaler.
|
||||||
func (h *HPAV1) Get(ns, n string) (interface{}, error) {
|
func (h *HorizontalPodAutoscalerV1) Get(ns, n string) (interface{}, error) {
|
||||||
return h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
return h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all HPAs in a given namespace.
|
// List all HorizontalPodAutoscalers in a given namespace.
|
||||||
func (h *HPAV1) List(ns string) (Collection, error) {
|
func (h *HorizontalPodAutoscalerV1) List(ns string) (Collection, error) {
|
||||||
rr, err := h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: h.labelSelector,
|
||||||
|
FieldSelector: h.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +37,7 @@ func (h *HPAV1) List(ns string) (Collection, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a HPA.
|
// Delete a HorizontalPodAutoscaler.
|
||||||
func (h *HPAV1) Delete(ns, n string) error {
|
func (h *HorizontalPodAutoscalerV1) Delete(ns, n string) error {
|
||||||
return h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
return h.DialOrDie().AutoscalingV1().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,29 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HPAV2Beta1 represents am HorizontalPodAutoscaler.
|
// HorizontalPodAutoscalerV2Beta1 represents am HorizontalPodAutoscaler.
|
||||||
type HPAV2Beta1 struct {
|
type HorizontalPodAutoscalerV2Beta1 struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV2Beta1 returns a new HPA.
|
// NewHorizontalPodAutoscalerV2Beta1 returns a new HorizontalPodAutoscaler.
|
||||||
func NewHPAV2Beta1(c Connection) Cruder {
|
func NewHorizontalPodAutoscalerV2Beta1(c Connection) *HorizontalPodAutoscalerV2Beta1 {
|
||||||
return &HPAV2Beta1{c}
|
return &HorizontalPodAutoscalerV2Beta1{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a HPA.
|
// Get a HorizontalPodAutoscaler.
|
||||||
func (h *HPAV2Beta1) Get(ns, n string) (interface{}, error) {
|
func (h *HorizontalPodAutoscalerV2Beta1) Get(ns, n string) (interface{}, error) {
|
||||||
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all HPAs in a given namespace.
|
// List all HorizontalPodAutoscalers in a given namespace.
|
||||||
func (h *HPAV2Beta1) List(ns string) (Collection, error) {
|
func (h *HorizontalPodAutoscalerV2Beta1) List(ns string) (Collection, error) {
|
||||||
rr, err := h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: h.labelSelector,
|
||||||
|
FieldSelector: h.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +37,7 @@ func (h *HPAV2Beta1) List(ns string) (Collection, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a HPA.
|
// Delete a HorizontalPodAutoscaler.
|
||||||
func (h *HPAV2Beta1) Delete(ns, n string) error {
|
func (h *HorizontalPodAutoscalerV2Beta1) Delete(ns, n string) error {
|
||||||
return h.DialOrDie().AutoscalingV2beta1().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
return h.DialOrDie().AutoscalingV2beta1().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,24 +6,29 @@ import (
|
||||||
|
|
||||||
var supportedAutoScalingAPIVersions = []string{"v2beta2", "v2beta1", "v1"}
|
var supportedAutoScalingAPIVersions = []string{"v2beta2", "v2beta1", "v1"}
|
||||||
|
|
||||||
// HPAV2Beta2 represents am HorizontalPodAutoscaler.
|
// HorizontalPodAutoscalerV2Beta2 represents am HorizontalPodAutoscaler.
|
||||||
type HPAV2Beta2 struct {
|
type HorizontalPodAutoscalerV2Beta2 struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV2Beta2 returns a new HPAV2Beta2.
|
// NewHorizontalPodAutoscalerV2Beta2 returns a new HorizontalPodAutoscalerV2Beta2.
|
||||||
func NewHPAV2Beta2(c Connection) Cruder {
|
func NewHorizontalPodAutoscalerV2Beta2(c Connection) *HorizontalPodAutoscalerV2Beta2 {
|
||||||
return &HPAV2Beta2{c}
|
return &HorizontalPodAutoscalerV2Beta2{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a HPAV2Beta2.
|
// Get a HorizontalPodAutoscalerV2Beta2.
|
||||||
func (h *HPAV2Beta2) Get(ns, n string) (interface{}, error) {
|
func (h *HorizontalPodAutoscalerV2Beta2) Get(ns, n string) (interface{}, error) {
|
||||||
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all HPAV2Beta2s in a given namespace.
|
// List all HorizontalPodAutoscalerV2Beta2s in a given namespace.
|
||||||
func (h *HPAV2Beta2) List(ns string) (Collection, error) {
|
func (h *HorizontalPodAutoscalerV2Beta2) List(ns string) (Collection, error) {
|
||||||
rr, err := h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: h.labelSelector,
|
||||||
|
FieldSelector: h.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +39,7 @@ func (h *HPAV2Beta2) List(ns string) (Collection, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a HPAV2Beta2.
|
// Delete a HorizontalPodAutoscalerV2Beta2.
|
||||||
func (h *HPAV2Beta2) Delete(ns, n string) error {
|
func (h *HorizontalPodAutoscalerV2Beta2) Delete(ns, n string) error {
|
||||||
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
return h.DialOrDie().AutoscalingV2beta2().HorizontalPodAutoscalers(ns).Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Ingress represents a Kubernetes Ingress.
|
// Ingress represents a Kubernetes Ingress.
|
||||||
type Ingress struct {
|
type Ingress struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIngress returns a new Ingress.
|
// NewIngress returns a new Ingress.
|
||||||
func NewIngress(c Connection) Cruder {
|
func NewIngress(c Connection) *Ingress {
|
||||||
return &Ingress{c}
|
return &Ingress{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Ingress.
|
// Get a Ingress.
|
||||||
|
|
@ -21,7 +22,11 @@ func (i *Ingress) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Ingresss in a given namespace.
|
// List all Ingresss in a given namespace.
|
||||||
func (i *Ingress) List(ns string) (Collection, error) {
|
func (i *Ingress) List(ns string) (Collection, error) {
|
||||||
rr, err := i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: i.labelSelector,
|
||||||
|
FieldSelector: i.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := i.DialOrDie().ExtensionsV1beta1().Ingresses(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
type (
|
type (
|
||||||
// Job represents a Kubernetes Job.
|
// Job represents a Kubernetes Job.
|
||||||
Job struct {
|
Job struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -23,8 +24,8 @@ type (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewJob returns a new Job.
|
// NewJob returns a new Job.
|
||||||
func NewJob(c Connection) Cruder {
|
func NewJob(c Connection) *Job {
|
||||||
return &Job{c}
|
return &Job{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Job.
|
// Get a Job.
|
||||||
|
|
@ -34,7 +35,11 @@ func (j *Job) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Jobs in a given namespace.
|
// List all Jobs in a given namespace.
|
||||||
func (j *Job) List(ns string) (Collection, error) {
|
func (j *Job) List(ns string) (Collection, error) {
|
||||||
rr, err := j.DialOrDie().BatchV1().Jobs(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: j.labelSelector,
|
||||||
|
FieldSelector: j.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := j.DialOrDie().BatchV1().Jobs(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +62,7 @@ func (j *Job) Containers(ns, n string, includeInit bool) ([]string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewPod(j).(Loggable).Containers(ns, pod, includeInit)
|
return NewPod(j).Containers(ns, pod, includeInit)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs fetch container logs for a given job and container.
|
// Logs fetch container logs for a given job and container.
|
||||||
|
|
@ -66,7 +71,7 @@ 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(j).(Loggable).Logs(ns, pod, co, lines, prev)
|
return NewPod(j).Logs(ns, pod, co, lines, prev)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events retrieved jobs events.
|
// Events retrieved jobs events.
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type (
|
type (
|
||||||
// MetricsServer serves cluster metrics for nodes and pods.
|
// MetricsServer serves cluster metrics for nodes and pods.
|
||||||
MetricsServer struct {
|
MetricsServer struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +44,7 @@ type (
|
||||||
|
|
||||||
// NewMetricsServer return a metric server instance.
|
// NewMetricsServer return a metric server instance.
|
||||||
func NewMetricsServer(c Connection) *MetricsServer {
|
func NewMetricsServer(c Connection) *MetricsServer {
|
||||||
return &MetricsServer{c}
|
return &MetricsServer{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NodesMetrics retrieves metrics for a given set of nodes.
|
// NodesMetrics retrieves metrics for a given set of nodes.
|
||||||
|
|
@ -51,16 +52,16 @@ func (m *MetricsServer) NodesMetrics(nodes []v1.Node, metrics []mv1beta1.NodeMet
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
mmx[n.Name] = NodeMetrics{
|
mmx[n.Name] = NodeMetrics{
|
||||||
AvailCPU: n.Status.Allocatable.Cpu().MilliValue(),
|
AvailCPU: n.Status.Allocatable.Cpu().MilliValue(),
|
||||||
AvailMEM: asMi(n.Status.Allocatable.Memory().Value()),
|
AvailMEM: ToMB(n.Status.Allocatable.Memory().Value()),
|
||||||
TotalCPU: n.Status.Capacity.Cpu().MilliValue(),
|
TotalCPU: n.Status.Capacity.Cpu().MilliValue(),
|
||||||
TotalMEM: asMi(n.Status.Capacity.Memory().Value()),
|
TotalMEM: ToMB(n.Status.Capacity.Memory().Value()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range metrics {
|
for _, c := range metrics {
|
||||||
if mx, ok := mmx[c.Name]; ok {
|
if mx, ok := mmx[c.Name]; ok {
|
||||||
mx.CurrentCPU = c.Usage.Cpu().MilliValue()
|
mx.CurrentCPU = c.Usage.Cpu().MilliValue()
|
||||||
mx.CurrentMEM = asMi(c.Usage.Memory().Value())
|
mx.CurrentMEM = ToMB(c.Usage.Memory().Value())
|
||||||
mmx[c.Name] = mx
|
mmx[c.Name] = mx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -73,16 +74,16 @@ func (m *MetricsServer) ClusterLoad(nodes []v1.Node, metrics []mv1beta1.NodeMetr
|
||||||
for _, n := range nodes {
|
for _, n := range nodes {
|
||||||
nodeMetrics[n.Name] = NodeMetrics{
|
nodeMetrics[n.Name] = NodeMetrics{
|
||||||
AvailCPU: n.Status.Allocatable.Cpu().MilliValue(),
|
AvailCPU: n.Status.Allocatable.Cpu().MilliValue(),
|
||||||
AvailMEM: asMi(n.Status.Allocatable.Memory().Value()),
|
AvailMEM: ToMB(n.Status.Allocatable.Memory().Value()),
|
||||||
TotalCPU: n.Status.Capacity.Cpu().MilliValue(),
|
TotalCPU: n.Status.Capacity.Cpu().MilliValue(),
|
||||||
TotalMEM: asMi(n.Status.Capacity.Memory().Value()),
|
TotalMEM: ToMB(n.Status.Capacity.Memory().Value()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, mx := range metrics {
|
for _, mx := range metrics {
|
||||||
if m, ok := nodeMetrics[mx.Name]; ok {
|
if m, ok := nodeMetrics[mx.Name]; ok {
|
||||||
m.CurrentCPU = mx.Usage.Cpu().MilliValue()
|
m.CurrentCPU = mx.Usage.Cpu().MilliValue()
|
||||||
m.CurrentMEM = asMi(mx.Usage.Memory().Value())
|
m.CurrentMEM = ToMB(mx.Usage.Memory().Value())
|
||||||
nodeMetrics[mx.Name] = m
|
nodeMetrics[mx.Name] = m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +134,7 @@ func (m *MetricsServer) PodsMetrics(pods []mv1beta1.PodMetrics, mmx PodsMetrics)
|
||||||
var mx PodMetrics
|
var mx PodMetrics
|
||||||
for _, c := range p.Containers {
|
for _, c := range p.Containers {
|
||||||
mx.CurrentCPU += c.Usage.Cpu().MilliValue()
|
mx.CurrentCPU += c.Usage.Cpu().MilliValue()
|
||||||
mx.CurrentMEM += asMi(c.Usage.Memory().Value())
|
mx.CurrentMEM += ToMB(c.Usage.Memory().Value())
|
||||||
}
|
}
|
||||||
mmx[p.Namespace+"/"+p.Name] = mx
|
mmx[p.Namespace+"/"+p.Name] = mx
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Node represents a Kubernetes node.
|
// Node represents a Kubernetes node.
|
||||||
type Node struct {
|
type Node struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNode returns a new Node.
|
// NewNode returns a new Node.
|
||||||
func NewNode(c Connection) Cruder {
|
func NewNode(c Connection) *Node {
|
||||||
return &Node{c}
|
return &Node{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a node.
|
// Get a node.
|
||||||
|
|
@ -21,7 +22,11 @@ func (n *Node) Get(_, name string) (interface{}, error) {
|
||||||
|
|
||||||
// List all nodes on the cluster.
|
// List all nodes on the cluster.
|
||||||
func (n *Node) List(_ string) (Collection, error) {
|
func (n *Node) List(_ string) (Collection, error) {
|
||||||
rr, err := n.DialOrDie().CoreV1().Nodes().List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: n.labelSelector,
|
||||||
|
FieldSelector: n.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := n.DialOrDie().CoreV1().Nodes().List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Namespace represents a Kubernetes namespace.
|
// Namespace represents a Kubernetes namespace.
|
||||||
type Namespace struct {
|
type Namespace struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewNamespace returns a new Namespace.
|
// NewNamespace returns a new Namespace.
|
||||||
func NewNamespace(c Connection) Cruder {
|
func NewNamespace(c Connection) *Namespace {
|
||||||
return &Namespace{c}
|
return &Namespace{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a active namespace.
|
// Get a active namespace.
|
||||||
|
|
@ -21,7 +22,11 @@ func (n *Namespace) Get(_, name string) (interface{}, error) {
|
||||||
|
|
||||||
// List all active namespaces on the cluster.
|
// List all active namespaces on the cluster.
|
||||||
func (n *Namespace) List(_ string) (Collection, error) {
|
func (n *Namespace) List(_ string) (Collection, error) {
|
||||||
rr, err := n.DialOrDie().CoreV1().Namespaces().List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: n.labelSelector,
|
||||||
|
FieldSelector: n.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := n.DialOrDie().CoreV1().Namespaces().List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PodDisruptionBudget represents a PodDisruptionBudget Kubernetes resource.
|
// PodDisruptionBudget represents a Kubernetes PodDisruptionBudget.
|
||||||
type PodDisruptionBudget struct {
|
type PodDisruptionBudget struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPodDisruptionBudget returns a new PodDisruptionBudget.
|
// NewPodDisruptionBudget returns a new PodDisruptionBudget.
|
||||||
func NewPodDisruptionBudget(c Connection) Cruder {
|
func NewPodDisruptionBudget(c Connection) *PodDisruptionBudget {
|
||||||
return &PodDisruptionBudget{c}
|
return &PodDisruptionBudget{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a pdb.
|
// Get a pdb.
|
||||||
|
|
@ -21,7 +22,11 @@ func (p *PodDisruptionBudget) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all pdbs in a given namespace.
|
// List all pdbs in a given namespace.
|
||||||
func (p *PodDisruptionBudget) List(ns string) (Collection, error) {
|
func (p *PodDisruptionBudget) List(ns string) (Collection, error) {
|
||||||
rr, err := p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: p.labelSelector,
|
||||||
|
FieldSelector: p.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := p.DialOrDie().PolicyV1beta1().PodDisruptionBudgets(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,15 @@ import (
|
||||||
|
|
||||||
const defaultKillGrace int64 = 5
|
const defaultKillGrace int64 = 5
|
||||||
|
|
||||||
// Pod represents a Kubernetes resource.
|
// Pod represents a Kubernetes Pod.
|
||||||
type Pod struct {
|
type Pod struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPod returns a new Pod.
|
// NewPod returns a new Pod.
|
||||||
func NewPod(c Connection) Cruder {
|
func NewPod(c Connection) *Pod {
|
||||||
return &Pod{c}
|
return &Pod{base: &base{}, Connection: c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a pod.
|
// Get a pod.
|
||||||
|
|
@ -25,7 +26,13 @@ func (p *Pod) Get(ns, name string) (interface{}, error) {
|
||||||
|
|
||||||
// List all pods in a given namespace.
|
// List all pods in a given namespace.
|
||||||
func (p *Pod) List(ns string) (Collection, error) {
|
func (p *Pod) List(ns string) (Collection, error) {
|
||||||
rr, err := p.DialOrDie().CoreV1().Pods(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: p.labelSelector,
|
||||||
|
FieldSelector: p.fieldSelector,
|
||||||
|
}
|
||||||
|
// FieldSelector: "spec.nodeName=gke-k9s-default-pool-0fa2fb89-lbtf",
|
||||||
|
|
||||||
|
rr, err := p.DialOrDie().CoreV1().Pods(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,29 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PV represents a Kubernetes PersistentVolume.
|
// PeristentVolume represents a Kubernetes PersistentVolume.
|
||||||
type PV struct {
|
type PeristentVolume struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPV returns a new PV.
|
// NewPersistentVolume returns a new PeristentVolume.
|
||||||
func NewPV(c Connection) Cruder {
|
func NewPersistentVolume(c Connection) *PeristentVolume {
|
||||||
return &PV{c}
|
return &PeristentVolume{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a PV.
|
// Get a PeristentVolume.
|
||||||
func (p *PV) Get(_, n string) (interface{}, error) {
|
func (p *PeristentVolume) Get(_, n string) (interface{}, error) {
|
||||||
return p.DialOrDie().CoreV1().PersistentVolumes().Get(n, metav1.GetOptions{})
|
return p.DialOrDie().CoreV1().PersistentVolumes().Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all PVs in a given namespace.
|
// List all PeristentVolumes in a given namespace.
|
||||||
func (p *PV) List(_ string) (Collection, error) {
|
func (p *PeristentVolume) List(_ string) (Collection, error) {
|
||||||
rr, err := p.DialOrDie().CoreV1().PersistentVolumes().List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: p.labelSelector,
|
||||||
|
FieldSelector: p.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := p.DialOrDie().CoreV1().PersistentVolumes().List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +39,7 @@ func (p *PV) List(_ string) (Collection, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a PV.
|
// Delete a PeristentVolume.
|
||||||
func (p *PV) Delete(_, n string) error {
|
func (p *PeristentVolume) Delete(_, n string) error {
|
||||||
return p.DialOrDie().CoreV1().PersistentVolumes().Delete(n, nil)
|
return p.DialOrDie().CoreV1().PersistentVolumes().Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,29 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PVC represents a Kubernetes service.
|
// PersistentVolumeClaim represents a Kubernetes PersistentVolumeClaim.
|
||||||
type PVC struct {
|
type PersistentVolumeClaim struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPVC returns a new PVC.
|
// NewPersistentVolumeClaim returns a new PersistentVolumeClaim.
|
||||||
func NewPVC(c Connection) Cruder {
|
func NewPersistentVolumeClaim(c Connection) *PersistentVolumeClaim {
|
||||||
return &PVC{c}
|
return &PersistentVolumeClaim{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a PVC.
|
// Get a PersistentVolumeClaim.
|
||||||
func (p *PVC) Get(ns, n string) (interface{}, error) {
|
func (p *PersistentVolumeClaim) Get(ns, n string) (interface{}, error) {
|
||||||
return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Get(n, metav1.GetOptions{})
|
return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all PVCs in a given namespace.
|
// List all PersistentVolumeClaims in a given namespace.
|
||||||
func (p *PVC) List(ns string) (Collection, error) {
|
func (p *PersistentVolumeClaim) List(ns string) (Collection, error) {
|
||||||
rr, err := p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: p.labelSelector,
|
||||||
|
FieldSelector: p.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +38,7 @@ func (p *PVC) List(ns string) (Collection, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a PVC.
|
// Delete a PersistentVolumeClaim.
|
||||||
func (p *PVC) Delete(ns, n string) error {
|
func (p *PersistentVolumeClaim) Delete(ns, n string) error {
|
||||||
return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Delete(n, nil)
|
return p.DialOrDie().CoreV1().PersistentVolumeClaims(ns).Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,15 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReplicationController represents a Kubernetes service.
|
// ReplicationController represents a Kubernetes ReplicationController.
|
||||||
type ReplicationController struct {
|
type ReplicationController struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReplicationController returns a new ReplicationController.
|
// NewReplicationController returns a new ReplicationController.
|
||||||
func NewReplicationController(c Connection) Cruder {
|
func NewReplicationController(c Connection) *ReplicationController {
|
||||||
return &ReplicationController{c}
|
return &ReplicationController{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a RC.
|
// Get a RC.
|
||||||
|
|
@ -21,7 +22,11 @@ func (r *ReplicationController) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all RCs in a given namespace.
|
// List all RCs in a given namespace.
|
||||||
func (r *ReplicationController) List(ns string) (Collection, error) {
|
func (r *ReplicationController) List(ns string) (Collection, error) {
|
||||||
rr, err := r.DialOrDie().Core().ReplicationControllers(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: r.labelSelector,
|
||||||
|
FieldSelector: r.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := r.DialOrDie().Core().ReplicationControllers(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,14 +14,15 @@ import (
|
||||||
|
|
||||||
// Resource represents a Kubernetes Resource
|
// Resource represents a Kubernetes Resource
|
||||||
type Resource struct {
|
type Resource struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
|
|
||||||
group, version, name string
|
group, version, name string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewResource returns a new Resource.
|
// NewResource returns a new Resource.
|
||||||
func NewResource(c Connection, group, version, name string) Cruder {
|
func NewResource(c Connection, group, version, name string) *Resource {
|
||||||
return &Resource{Connection: c, group: group, version: version, name: name}
|
return &Resource{base: &base{}, Connection: c, group: group, version: version, name: name}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns info about apigroup.
|
// GetInfo returns info about apigroup.
|
||||||
|
|
@ -29,7 +30,7 @@ func (r *Resource) GetInfo() (string, string, string) {
|
||||||
return r.group, r.version, r.name
|
return r.group, r.version, r.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resource) base() dynamic.NamespaceableResourceInterface {
|
func (r *Resource) nsRes() dynamic.NamespaceableResourceInterface {
|
||||||
g := schema.GroupVersionResource{
|
g := schema.GroupVersionResource{
|
||||||
Group: r.group,
|
Group: r.group,
|
||||||
Version: r.version,
|
Version: r.version,
|
||||||
|
|
@ -40,7 +41,7 @@ func (r *Resource) base() dynamic.NamespaceableResourceInterface {
|
||||||
|
|
||||||
// Get a Resource.
|
// Get a Resource.
|
||||||
func (r *Resource) Get(ns, n string) (interface{}, error) {
|
func (r *Resource) Get(ns, n string) (interface{}, error) {
|
||||||
return r.base().Namespace(ns).Get(n, metav1.GetOptions{})
|
return r.nsRes().Namespace(ns).Get(n, metav1.GetOptions{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// List all Resources in a given namespace.
|
// List all Resources in a given namespace.
|
||||||
|
|
@ -54,7 +55,7 @@ func (r *Resource) List(ns string) (Collection, error) {
|
||||||
|
|
||||||
// Delete a Resource.
|
// Delete a Resource.
|
||||||
func (r *Resource) Delete(ns, n string) error {
|
func (r *Resource) Delete(ns, n string) error {
|
||||||
return r.base().Namespace(ns).Delete(n, nil)
|
return r.nsRes().Namespace(ns).Delete(n, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,13 @@ import (
|
||||||
|
|
||||||
// Role represents a Kubernetes Role.
|
// Role represents a Kubernetes Role.
|
||||||
type Role struct {
|
type Role struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRole returns a new Role.
|
// NewRole returns a new Role.
|
||||||
func NewRole(c Connection) Cruder {
|
func NewRole(c Connection) *Role {
|
||||||
return &Role{c}
|
return &Role{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Role.
|
// Get a Role.
|
||||||
|
|
@ -22,7 +23,11 @@ func (r *Role) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Roles in a given namespace.
|
// List all Roles in a given namespace.
|
||||||
func (r *Role) List(ns string) (Collection, error) {
|
func (r *Role) List(ns string) (Collection, error) {
|
||||||
rr, err := r.DialOrDie().RbacV1().Roles(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: r.labelSelector,
|
||||||
|
FieldSelector: r.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := r.DialOrDie().RbacV1().Roles(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,13 @@ 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 {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRoleBinding returns a new RoleBinding.
|
// NewRoleBinding returns a new RoleBinding.
|
||||||
func NewRoleBinding(c Connection) Cruder {
|
func NewRoleBinding(c Connection) *RoleBinding {
|
||||||
return &RoleBinding{c}
|
return &RoleBinding{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a RoleBinding.
|
// Get a RoleBinding.
|
||||||
|
|
@ -19,7 +20,11 @@ func (r *RoleBinding) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all RoleBindings in a given namespace.
|
// List all RoleBindings in a given namespace.
|
||||||
func (r *RoleBinding) List(ns string) (Collection, error) {
|
func (r *RoleBinding) List(ns string) (Collection, error) {
|
||||||
rr, err := r.DialOrDie().RbacV1().RoleBindings(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: r.labelSelector,
|
||||||
|
FieldSelector: r.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := r.DialOrDie().RbacV1().RoleBindings(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// ReplicaSet represents a Kubernetes ReplicaSet.
|
// ReplicaSet represents a Kubernetes ReplicaSet.
|
||||||
type ReplicaSet struct {
|
type ReplicaSet struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReplicaSet returns a new ReplicaSet.
|
// NewReplicaSet returns a new ReplicaSet.
|
||||||
func NewReplicaSet(c Connection) Cruder {
|
func NewReplicaSet(c Connection) *ReplicaSet {
|
||||||
return &ReplicaSet{c}
|
return &ReplicaSet{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a ReplicaSet.
|
// Get a ReplicaSet.
|
||||||
|
|
@ -21,7 +22,11 @@ func (r *ReplicaSet) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all ReplicaSets in a given namespace.
|
// List all ReplicaSets in a given namespace.
|
||||||
func (r *ReplicaSet) List(ns string) (Collection, error) {
|
func (r *ReplicaSet) List(ns string) (Collection, error) {
|
||||||
rr, err := r.DialOrDie().Apps().ReplicaSets(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: r.labelSelector,
|
||||||
|
FieldSelector: r.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := r.DialOrDie().Apps().ReplicaSets(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// ServiceAccount manages a Kubernetes ServiceAccount.
|
// ServiceAccount manages a Kubernetes ServiceAccount.
|
||||||
type ServiceAccount struct {
|
type ServiceAccount struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceAccount instantiates a new ServiceAccount.
|
// NewServiceAccount instantiates a new ServiceAccount.
|
||||||
func NewServiceAccount(c Connection) Cruder {
|
func NewServiceAccount(c Connection) *ServiceAccount {
|
||||||
return &ServiceAccount{c}
|
return &ServiceAccount{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a ServiceAccount.
|
// Get a ServiceAccount.
|
||||||
|
|
@ -21,7 +22,11 @@ func (s *ServiceAccount) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all ServiceAccounts in a given namespace.
|
// List all ServiceAccounts in a given namespace.
|
||||||
func (s *ServiceAccount) List(ns string) (Collection, error) {
|
func (s *ServiceAccount) List(ns string) (Collection, error) {
|
||||||
rr, err := s.DialOrDie().CoreV1().ServiceAccounts(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: s.labelSelector,
|
||||||
|
FieldSelector: s.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := s.DialOrDie().CoreV1().ServiceAccounts(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Secret represents a Kubernetes Secret.
|
// Secret represents a Kubernetes Secret.
|
||||||
type Secret struct {
|
type Secret struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSecret returns a new Secret.
|
// NewSecret returns a new Secret.
|
||||||
func NewSecret(c Connection) Cruder {
|
func NewSecret(c Connection) *Secret {
|
||||||
return &Secret{c}
|
return &Secret{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a Secret.
|
// Get a Secret.
|
||||||
|
|
@ -21,7 +22,11 @@ func (s *Secret) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Secrets in a given namespace.
|
// List all Secrets in a given namespace.
|
||||||
func (s *Secret) List(ns string) (Collection, error) {
|
func (s *Secret) List(ns string) (Collection, error) {
|
||||||
rr, err := s.DialOrDie().CoreV1().Secrets(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: s.labelSelector,
|
||||||
|
FieldSelector: s.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := s.DialOrDie().CoreV1().Secrets(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// StatefulSet manages a Kubernetes StatefulSet.
|
// StatefulSet manages a Kubernetes StatefulSet.
|
||||||
type StatefulSet struct {
|
type StatefulSet struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStatefulSet instantiates a new StatefulSet.
|
// NewStatefulSet instantiates a new StatefulSet.
|
||||||
func NewStatefulSet(c Connection) Cruder {
|
func NewStatefulSet(c Connection) *StatefulSet {
|
||||||
return &StatefulSet{c}
|
return &StatefulSet{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a StatefulSet.
|
// Get a StatefulSet.
|
||||||
|
|
@ -21,7 +22,11 @@ func (s *StatefulSet) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all StatefulSets in a given namespace.
|
// List all StatefulSets in a given namespace.
|
||||||
func (s *StatefulSet) List(ns string) (Collection, error) {
|
func (s *StatefulSet) List(ns string) (Collection, error) {
|
||||||
rr, err := s.DialOrDie().AppsV1().StatefulSets(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: s.labelSelector,
|
||||||
|
FieldSelector: s.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := s.DialOrDie().AppsV1().StatefulSets(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,13 @@ import (
|
||||||
|
|
||||||
// Service represents a Kubernetes Service.
|
// Service represents a Kubernetes Service.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
|
*base
|
||||||
Connection
|
Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewService returns a new Service.
|
// NewService returns a new Service.
|
||||||
func NewService(c Connection) Cruder {
|
func NewService(c Connection) *Service {
|
||||||
return &Service{c}
|
return &Service{&base{}, c}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a service.
|
// Get a service.
|
||||||
|
|
@ -21,7 +22,11 @@ func (s *Service) Get(ns, n string) (interface{}, error) {
|
||||||
|
|
||||||
// List all Services in a given namespace.
|
// List all Services in a given namespace.
|
||||||
func (s *Service) List(ns string) (Collection, error) {
|
func (s *Service) List(ns string) (Collection, error) {
|
||||||
rr, err := s.DialOrDie().CoreV1().Services(ns).List(metav1.ListOptions{})
|
opts := metav1.ListOptions{
|
||||||
|
LabelSelector: s.labelSelector,
|
||||||
|
FieldSelector: s.fieldSelector,
|
||||||
|
}
|
||||||
|
rr, err := s.DialOrDie().CoreV1().Services(ns).List(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ type (
|
||||||
Get(ns string, name string) (interface{}, error)
|
Get(ns string, name string) (interface{}, error)
|
||||||
List(ns string) (k8s.Collection, error)
|
List(ns string) (k8s.Collection, error)
|
||||||
Delete(ns string, name string) error
|
Delete(ns string, name string) error
|
||||||
|
SetLabelSelector(string)
|
||||||
|
SetFieldSelector(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection represents a Kubenetes apiserver connection.
|
// Connection represents a Kubenetes apiserver connection.
|
||||||
|
|
@ -45,6 +47,16 @@ func NewBase(c Connection, r Cruder) *Base {
|
||||||
return &Base{Connection: c, Resource: r}
|
return &Base{Connection: c, Resource: r}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFieldSelector refines query results via selector.
|
||||||
|
func (b *Base) SetFieldSelector(s string) {
|
||||||
|
b.Resource.SetFieldSelector(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabelSelector refines query results via labels.
|
||||||
|
func (b *Base) SetLabelSelector(s string) {
|
||||||
|
b.Resource.SetLabelSelector(s)
|
||||||
|
}
|
||||||
|
|
||||||
// Name returns the resource namespaced name.
|
// Name returns the resource namespaced name.
|
||||||
func (b *Base) Name() string {
|
func (b *Base) Name() string {
|
||||||
return b.path
|
return b.path
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,262 @@
|
||||||
|
package resource
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/k8s"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
v1 "k8s.io/api/core/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Container represents a container on a pod.
|
||||||
|
Container struct {
|
||||||
|
*Base
|
||||||
|
|
||||||
|
pod *v1.Pod
|
||||||
|
isInit bool
|
||||||
|
instance v1.Container
|
||||||
|
MetricsServer MetricsServer
|
||||||
|
metrics k8s.PodMetrics
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewContainerList returns a collection of container.
|
||||||
|
func NewContainerList(c Connection, mx MetricsServer, pod *v1.Pod) List {
|
||||||
|
return NewList(
|
||||||
|
"",
|
||||||
|
"co",
|
||||||
|
NewContainer(c, mx, pod),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainer returns a new set of containers.
|
||||||
|
func NewContainer(c Connection, mx MetricsServer, pod *v1.Pod) *Container {
|
||||||
|
co := Container{
|
||||||
|
Base: &Base{Connection: c, Resource: k8s.NewPod(c)},
|
||||||
|
pod: pod,
|
||||||
|
MetricsServer: mx,
|
||||||
|
metrics: k8s.PodMetrics{},
|
||||||
|
}
|
||||||
|
co.Factory = &co
|
||||||
|
|
||||||
|
return &co
|
||||||
|
}
|
||||||
|
|
||||||
|
// New builds a new Container instance from a k8s resource.
|
||||||
|
func (r *Container) New(i interface{}) Columnar {
|
||||||
|
co := NewContainer(r.Connection, r.MetricsServer, r.pod)
|
||||||
|
co.instance = i.(v1.Container)
|
||||||
|
co.path = r.namespacedName(r.pod.ObjectMeta) + ":" + co.instance.Name
|
||||||
|
|
||||||
|
return co
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metrics retrieves cpu/mem resource consumption on associated pod.
|
||||||
|
func (r *Container) Metrics() k8s.PodMetrics {
|
||||||
|
return r.metrics
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMetrics set the current k8s resource metrics on associated pod.
|
||||||
|
func (r *Container) SetMetrics(m k8s.PodMetrics) {
|
||||||
|
r.metrics = m
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal resource to yaml.
|
||||||
|
func (r *Container) Marshal(path string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs tails a given container logs
|
||||||
|
func (r *Container) Logs(c chan<- string, ns, n, co string, lines int64, prev bool) (context.CancelFunc, error) {
|
||||||
|
req := r.Resource.(k8s.Loggable).Logs(ns, n, co, lines, prev)
|
||||||
|
ctx, cancel := context.WithCancel(context.TODO())
|
||||||
|
req.Context(ctx)
|
||||||
|
|
||||||
|
blocked := true
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-time.After(defaultTimeout):
|
||||||
|
if blocked {
|
||||||
|
close(c)
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// This call will block if nothing is in the stream!!
|
||||||
|
stream, err := req.Stream()
|
||||||
|
blocked = false
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Msgf("Tail logs failed `%s/%s:%s -- %v", ns, n, co, err)
|
||||||
|
return cancel, fmt.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
stream.Close()
|
||||||
|
cancel()
|
||||||
|
close(c)
|
||||||
|
}()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(stream)
|
||||||
|
for scanner.Scan() {
|
||||||
|
c <- scanner.Text()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return cancel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List resources for a given namespace.
|
||||||
|
func (r *Container) List(ns string) (Columnars, error) {
|
||||||
|
icos := r.pod.Spec.InitContainers
|
||||||
|
cos := r.pod.Spec.Containers
|
||||||
|
|
||||||
|
cc := make(Columnars, 0, len(icos)+len(cos))
|
||||||
|
for _, co := range icos {
|
||||||
|
ci := r.New(co)
|
||||||
|
ci.(*Container).isInit = true
|
||||||
|
cc = append(cc, ci)
|
||||||
|
}
|
||||||
|
for _, co := range cos {
|
||||||
|
cc = append(cc, r.New(co))
|
||||||
|
}
|
||||||
|
|
||||||
|
return cc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header return resource header.
|
||||||
|
func (*Container) Header(ns string) Row {
|
||||||
|
hh := Row{}
|
||||||
|
|
||||||
|
return append(hh,
|
||||||
|
"NAME",
|
||||||
|
"IMAGE",
|
||||||
|
"READY",
|
||||||
|
"STATE",
|
||||||
|
"RS",
|
||||||
|
"LPROB",
|
||||||
|
"RPROB",
|
||||||
|
"CPU",
|
||||||
|
"MEM",
|
||||||
|
"RCPU",
|
||||||
|
"RMEM",
|
||||||
|
"AGE",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields retrieves displayable fields.
|
||||||
|
func (r *Container) Fields(ns string) Row {
|
||||||
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
|
i := r.instance
|
||||||
|
|
||||||
|
mxs, _ := r.MetricsServer.FetchPodsMetrics(r.pod.Namespace)
|
||||||
|
|
||||||
|
var cpu, mem string
|
||||||
|
for _, mx := range mxs {
|
||||||
|
if mx.Name != r.pod.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, co := range mx.Containers {
|
||||||
|
if co.Name != i.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cpu, mem = toRes(co.Usage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rcpu, rmem := resources(i)
|
||||||
|
|
||||||
|
var cs *v1.ContainerStatus
|
||||||
|
for _, c := range r.pod.Status.ContainerStatuses {
|
||||||
|
if c.Name != i.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cs = &c
|
||||||
|
}
|
||||||
|
if cs == nil {
|
||||||
|
for _, c := range r.pod.Status.InitContainerStatuses {
|
||||||
|
if c.Name != i.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cs = &c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(ff,
|
||||||
|
i.Name,
|
||||||
|
i.Image,
|
||||||
|
boolToStr(cs.Ready),
|
||||||
|
toState(cs.State),
|
||||||
|
strconv.Itoa(int(cs.RestartCount)),
|
||||||
|
probe(i.LivenessProbe),
|
||||||
|
probe(i.ReadinessProbe),
|
||||||
|
cpu,
|
||||||
|
mem,
|
||||||
|
rcpu,
|
||||||
|
rmem,
|
||||||
|
toAge(r.pod.CreationTimestamp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Helpers...
|
||||||
|
|
||||||
|
func toState(s v1.ContainerState) string {
|
||||||
|
switch {
|
||||||
|
case s.Waiting != nil:
|
||||||
|
if s.Waiting.Reason != "" {
|
||||||
|
return s.Waiting.Reason
|
||||||
|
}
|
||||||
|
return "Waiting"
|
||||||
|
|
||||||
|
case s.Terminated != nil:
|
||||||
|
if s.Terminated.Reason != "" {
|
||||||
|
return s.Terminated.Reason
|
||||||
|
}
|
||||||
|
return "Terminated"
|
||||||
|
case s.Running != nil:
|
||||||
|
return "Running"
|
||||||
|
default:
|
||||||
|
return MissingValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toRes(r v1.ResourceList) (string, string) {
|
||||||
|
cpu, mem := r[v1.ResourceCPU], r[v1.ResourceMemory]
|
||||||
|
|
||||||
|
return ToMillicore(cpu.MilliValue()), ToMi(k8s.ToMB(mem.Value()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func resources(c v1.Container) (cpu, mem string) {
|
||||||
|
req, lim := c.Resources.Requests, c.Resources.Limits
|
||||||
|
|
||||||
|
if len(req) == 0 {
|
||||||
|
if len(lim) != 0 {
|
||||||
|
return toRes(lim)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return toRes(req)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "0", "0"
|
||||||
|
}
|
||||||
|
|
||||||
|
func probe(p *v1.Probe) string {
|
||||||
|
if p == nil {
|
||||||
|
return "no"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "yes"
|
||||||
|
}
|
||||||
|
|
||||||
|
func asMi(v int64) float64 {
|
||||||
|
const megaByte = 1024 * 1024
|
||||||
|
|
||||||
|
return float64(v) / megaByte
|
||||||
|
}
|
||||||
|
|
@ -10,40 +10,40 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CRD tracks a kubernetes resource.
|
// CustomResourceDefinition tracks a kubernetes resource.
|
||||||
type CRD struct {
|
type CustomResourceDefinition struct {
|
||||||
*Base
|
*Base
|
||||||
instance *unstructured.Unstructured
|
instance *unstructured.Unstructured
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCRDList returns a new resource list.
|
// NewCustomResourceDefinitionList returns a new resource list.
|
||||||
func NewCRDList(c Connection, ns string) List {
|
func NewCustomResourceDefinitionList(c Connection, ns string) List {
|
||||||
return NewList(
|
return NewList(
|
||||||
NotNamespaced,
|
NotNamespaced,
|
||||||
"crd",
|
"crd",
|
||||||
NewCRD(c),
|
NewCustomResourceDefinition(c),
|
||||||
CRUDAccess|DescribeAccess,
|
CRUDAccess|DescribeAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCRD instantiates a new CRD.
|
// NewCustomResourceDefinition instantiates a new CustomResourceDefinition.
|
||||||
func NewCRD(c Connection) *CRD {
|
func NewCustomResourceDefinition(c Connection) *CustomResourceDefinition {
|
||||||
crd := &CRD{&Base{Connection: c, Resource: k8s.NewCRD(c)}, nil}
|
crd := &CustomResourceDefinition{&Base{Connection: c, Resource: k8s.NewCustomResourceDefinition(c)}, nil}
|
||||||
crd.Factory = crd
|
crd.Factory = crd
|
||||||
|
|
||||||
return crd
|
return crd
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new CRD instance from a k8s resource.
|
// New builds a new CustomResourceDefinition instance from a k8s resource.
|
||||||
func (r *CRD) New(i interface{}) Columnar {
|
func (r *CustomResourceDefinition) New(i interface{}) Columnar {
|
||||||
c := NewCRD(r.Connection)
|
c := NewCustomResourceDefinition(r.Connection)
|
||||||
switch instance := i.(type) {
|
switch instance := i.(type) {
|
||||||
case *unstructured.Unstructured:
|
case *unstructured.Unstructured:
|
||||||
c.instance = instance
|
c.instance = instance
|
||||||
case unstructured.Unstructured:
|
case unstructured.Unstructured:
|
||||||
c.instance = &instance
|
c.instance = &instance
|
||||||
default:
|
default:
|
||||||
log.Fatal().Msgf("unknown CRD type %#v", i)
|
log.Fatal().Msgf("unknown CustomResourceDefinition type %#v", i)
|
||||||
}
|
}
|
||||||
meta := c.instance.Object["metadata"].(map[string]interface{})
|
meta := c.instance.Object["metadata"].(map[string]interface{})
|
||||||
c.path = meta["name"].(string)
|
c.path = meta["name"].(string)
|
||||||
|
|
@ -52,7 +52,7 @@ func (r *CRD) New(i interface{}) Columnar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal a resource.
|
// Marshal a resource.
|
||||||
func (r *CRD) Marshal(path string) (string, error) {
|
func (r *CustomResourceDefinition) Marshal(path string) (string, error) {
|
||||||
ns, n := namespaced(path)
|
ns, n := namespaced(path)
|
||||||
i, err := r.Resource.Get(ns, n)
|
i, err := r.Resource.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -70,12 +70,12 @@ func (r *CRD) Marshal(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header return the resource header.
|
// Header return the resource header.
|
||||||
func (*CRD) Header(ns string) Row {
|
func (*CustomResourceDefinition) Header(ns string) Row {
|
||||||
return Row{"NAME", "AGE"}
|
return Row{"NAME", "AGE"}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields retrieves displayable fields.
|
// Fields retrieves displayable fields.
|
||||||
func (r *CRD) Fields(ns string) Row {
|
func (r *CustomResourceDefinition) Fields(ns string) Row {
|
||||||
ff := make(Row, 0, len(r.Header(ns)))
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
|
|
||||||
i := r.instance
|
i := r.instance
|
||||||
|
|
@ -89,7 +89,7 @@ func (r *CRD) Fields(ns string) Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtFields returns extended fields.
|
// ExtFields returns extended fields.
|
||||||
func (r *CRD) ExtFields() Properties {
|
func (r *CustomResourceDefinition) ExtFields() Properties {
|
||||||
var (
|
var (
|
||||||
pp = Properties{}
|
pp = Properties{}
|
||||||
i = r.instance
|
i = r.instance
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@ import (
|
||||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewCRDListWithArgs(ns string, r *resource.CRD) resource.List {
|
func NewCRDListWithArgs(ns string, r *resource.CustomResourceDefinition) resource.List {
|
||||||
return resource.NewList("-", "crd", r, resource.CRUDAccess|resource.DescribeAccess)
|
return resource.NewList("-", "crd", r, resource.CRUDAccess|resource.DescribeAccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCRDWithArgs(conn k8s.Connection, res resource.Cruder) *resource.CRD {
|
func NewCRDWithArgs(conn k8s.Connection, res resource.Cruder) *resource.CustomResourceDefinition {
|
||||||
r := &resource.CRD{Base: resource.NewBase(conn, res)}
|
r := &resource.CustomResourceDefinition{Base: resource.NewBase(conn, res)}
|
||||||
r.Factory = r
|
r.Factory = r
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
@ -130,12 +130,12 @@ func k8sCRDFull() *unstructured.Unstructured {
|
||||||
|
|
||||||
func newCRDFull() resource.Columnar {
|
func newCRDFull() resource.Columnar {
|
||||||
mc := NewMockConnection()
|
mc := NewMockConnection()
|
||||||
return resource.NewCRD(mc).New(k8sCRDFull())
|
return resource.NewCustomResourceDefinition(mc).New(k8sCRDFull())
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCRD() resource.Columnar {
|
func newCRD() resource.Columnar {
|
||||||
mc := NewMockConnection()
|
mc := NewMockConnection()
|
||||||
return resource.NewCRD(mc).New(k8sCRD())
|
return resource.NewCustomResourceDefinition(mc).New(k8sCRD())
|
||||||
}
|
}
|
||||||
|
|
||||||
func crdYaml() string {
|
func crdYaml() string {
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewHPAListWithArgs(ns string, r *resource.HPA) resource.List {
|
func NewHPAListWithArgs(ns string, r *resource.HorizontalPodAutoscaler) resource.List {
|
||||||
return resource.NewList(ns, "hpa", r, resource.AllVerbsAccess|resource.DescribeAccess)
|
return resource.NewList(ns, "hpa", r, resource.AllVerbsAccess|resource.DescribeAccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHPAWithArgs(conn k8s.Connection, res resource.Cruder) *resource.HPA {
|
func NewHPAWithArgs(conn k8s.Connection, res resource.Cruder) *resource.HorizontalPodAutoscaler {
|
||||||
r := &resource.HPA{Base: resource.NewBase(conn, res)}
|
r := &resource.HorizontalPodAutoscaler{Base: resource.NewBase(conn, res)}
|
||||||
r.Factory = r
|
r.Factory = r
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +127,7 @@ func k8sHPA() *autoscalingv2beta2.HorizontalPodAutoscaler {
|
||||||
|
|
||||||
func newHPA() resource.Columnar {
|
func newHPA() resource.Columnar {
|
||||||
mc := NewMockConnection()
|
mc := NewMockConnection()
|
||||||
return resource.NewHPA(mc).New(k8sHPA())
|
return resource.NewHorizontalPodAutoscaler(mc).New(k8sHPA())
|
||||||
}
|
}
|
||||||
|
|
||||||
func hpaYaml() string {
|
func hpaYaml() string {
|
||||||
|
|
|
||||||
|
|
@ -8,40 +8,40 @@ import (
|
||||||
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
autoscalingv1 "k8s.io/api/autoscaling/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HPAV1 tracks a kubernetes resource.
|
// HorizontalPodAutoscalerV1 tracks a kubernetes resource.
|
||||||
type HPAV1 struct {
|
type HorizontalPodAutoscalerV1 struct {
|
||||||
*Base
|
*Base
|
||||||
instance *autoscalingv1.HorizontalPodAutoscaler
|
instance *autoscalingv1.HorizontalPodAutoscaler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV1List returns a new resource list.
|
// NewHorizontalPodAutoscalerV1List returns a new resource list.
|
||||||
func NewHPAV1List(c Connection, ns string) List {
|
func NewHorizontalPodAutoscalerV1List(c Connection, ns string) List {
|
||||||
return NewList(
|
return NewList(
|
||||||
ns,
|
ns,
|
||||||
"hpa",
|
"hpa",
|
||||||
NewHPAV1(c),
|
NewHorizontalPodAutoscalerV1(c),
|
||||||
AllVerbsAccess|DescribeAccess,
|
AllVerbsAccess|DescribeAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV1 instantiates a new HPAV1.
|
// NewHorizontalPodAutoscalerV1 instantiates a new HorizontalPodAutoscalerV1.
|
||||||
func NewHPAV1(c Connection) *HPAV1 {
|
func NewHorizontalPodAutoscalerV1(c Connection) *HorizontalPodAutoscalerV1 {
|
||||||
hpa := &HPAV1{&Base{Connection: c, Resource: k8s.NewHPAV1(c)}, nil}
|
hpa := &HorizontalPodAutoscalerV1{&Base{Connection: c, Resource: k8s.NewHorizontalPodAutoscalerV1(c)}, nil}
|
||||||
hpa.Factory = hpa
|
hpa.Factory = hpa
|
||||||
|
|
||||||
return hpa
|
return hpa
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new HPAV1 instance from a k8s resource.
|
// New builds a new HorizontalPodAutoscalerV1 instance from a k8s resource.
|
||||||
func (r *HPAV1) New(i interface{}) Columnar {
|
func (r *HorizontalPodAutoscalerV1) New(i interface{}) Columnar {
|
||||||
c := NewHPAV1(r.Connection)
|
c := NewHorizontalPodAutoscalerV1(r.Connection)
|
||||||
switch instance := i.(type) {
|
switch instance := i.(type) {
|
||||||
case *autoscalingv1.HorizontalPodAutoscaler:
|
case *autoscalingv1.HorizontalPodAutoscaler:
|
||||||
c.instance = instance
|
c.instance = instance
|
||||||
case autoscalingv1.HorizontalPodAutoscaler:
|
case autoscalingv1.HorizontalPodAutoscaler:
|
||||||
c.instance = &instance
|
c.instance = &instance
|
||||||
default:
|
default:
|
||||||
log.Fatal().Msgf("unknown HPAV1 type %#v", i)
|
log.Fatal().Msgf("unknown HorizontalPodAutoscalerV1 type %#v", i)
|
||||||
}
|
}
|
||||||
c.path = c.namespacedName(c.instance.ObjectMeta)
|
c.path = c.namespacedName(c.instance.ObjectMeta)
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ func (r *HPAV1) New(i interface{}) Columnar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal resource to yaml.
|
// Marshal resource to yaml.
|
||||||
func (r *HPAV1) Marshal(path string) (string, error) {
|
func (r *HorizontalPodAutoscalerV1) Marshal(path string) (string, error) {
|
||||||
ns, n := namespaced(path)
|
ns, n := namespaced(path)
|
||||||
i, err := r.Resource.Get(ns, n)
|
i, err := r.Resource.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -64,7 +64,7 @@ func (r *HPAV1) Marshal(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header return resource header.
|
// Header return resource header.
|
||||||
func (*HPAV1) Header(ns string) Row {
|
func (*HorizontalPodAutoscalerV1) Header(ns string) Row {
|
||||||
hh := Row{}
|
hh := Row{}
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
hh = append(hh, "NAMESPACE")
|
hh = append(hh, "NAMESPACE")
|
||||||
|
|
@ -81,7 +81,7 @@ func (*HPAV1) Header(ns string) Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields retrieves displayable fields.
|
// Fields retrieves displayable fields.
|
||||||
func (r *HPAV1) Fields(ns string) Row {
|
func (r *HorizontalPodAutoscalerV1) Fields(ns string) Row {
|
||||||
ff := make(Row, 0, len(r.Header(ns)))
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
|
|
||||||
i := r.instance
|
i := r.instance
|
||||||
|
|
@ -103,7 +103,7 @@ func (r *HPAV1) Fields(ns string) Row {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func (r *HPAV1) toMetrics(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalingv1.HorizontalPodAutoscalerStatus) string {
|
func (r *HorizontalPodAutoscalerV1) toMetrics(spec autoscalingv1.HorizontalPodAutoscalerSpec, status autoscalingv1.HorizontalPodAutoscalerStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
if status.CurrentCPUUtilizationPercentage != nil {
|
if status.CurrentCPUUtilizationPercentage != nil {
|
||||||
current = strconv.Itoa(int(*status.CurrentCPUUtilizationPercentage)) + "%"
|
current = strconv.Itoa(int(*status.CurrentCPUUtilizationPercentage)) + "%"
|
||||||
|
|
|
||||||
|
|
@ -10,40 +10,40 @@ import (
|
||||||
autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
|
autoscalingv2beta1 "k8s.io/api/autoscaling/v2beta1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HPAV2Beta1 tracks a kubernetes resource.
|
// HorizontalPodAutoscalerV2Beta1 tracks a kubernetes resource.
|
||||||
type HPAV2Beta1 struct {
|
type HorizontalPodAutoscalerV2Beta1 struct {
|
||||||
*Base
|
*Base
|
||||||
instance *autoscalingv2beta1.HorizontalPodAutoscaler
|
instance *autoscalingv2beta1.HorizontalPodAutoscaler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV2Beta1List returns a new resource list.
|
// NewHorizontalPodAutoscalerV2Beta1List returns a new resource list.
|
||||||
func NewHPAV2Beta1List(c Connection, ns string) List {
|
func NewHorizontalPodAutoscalerV2Beta1List(c Connection, ns string) List {
|
||||||
return NewList(
|
return NewList(
|
||||||
ns,
|
ns,
|
||||||
"hpa",
|
"hpa",
|
||||||
NewHPAV2Beta1(c),
|
NewHorizontalPodAutoscalerV2Beta1(c),
|
||||||
AllVerbsAccess|DescribeAccess,
|
AllVerbsAccess|DescribeAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAV2Beta1 instantiates a new HPAV2Beta1.
|
// NewHorizontalPodAutoscalerV2Beta1 instantiates a new HorizontalPodAutoscalerV2Beta1.
|
||||||
func NewHPAV2Beta1(c Connection) *HPAV2Beta1 {
|
func NewHorizontalPodAutoscalerV2Beta1(c Connection) *HorizontalPodAutoscalerV2Beta1 {
|
||||||
hpa := &HPAV2Beta1{&Base{Connection: c, Resource: k8s.NewHPAV2Beta1(c)}, nil}
|
hpa := &HorizontalPodAutoscalerV2Beta1{&Base{Connection: c, Resource: k8s.NewHorizontalPodAutoscalerV2Beta1(c)}, nil}
|
||||||
hpa.Factory = hpa
|
hpa.Factory = hpa
|
||||||
|
|
||||||
return hpa
|
return hpa
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new HPAV2Beta1 instance from a k8s resource.
|
// New builds a new HorizontalPodAutoscalerV2Beta1 instance from a k8s resource.
|
||||||
func (r *HPAV2Beta1) New(i interface{}) Columnar {
|
func (r *HorizontalPodAutoscalerV2Beta1) New(i interface{}) Columnar {
|
||||||
c := NewHPAV2Beta1(r.Connection)
|
c := NewHorizontalPodAutoscalerV2Beta1(r.Connection)
|
||||||
switch instance := i.(type) {
|
switch instance := i.(type) {
|
||||||
case *autoscalingv2beta1.HorizontalPodAutoscaler:
|
case *autoscalingv2beta1.HorizontalPodAutoscaler:
|
||||||
c.instance = instance
|
c.instance = instance
|
||||||
case autoscalingv2beta1.HorizontalPodAutoscaler:
|
case autoscalingv2beta1.HorizontalPodAutoscaler:
|
||||||
c.instance = &instance
|
c.instance = &instance
|
||||||
default:
|
default:
|
||||||
log.Fatal().Msgf("unknown HPAV2Beta1 type %#v", i)
|
log.Fatal().Msgf("unknown HorizontalPodAutoscalerV2Beta1 type %#v", i)
|
||||||
}
|
}
|
||||||
c.path = c.namespacedName(c.instance.ObjectMeta)
|
c.path = c.namespacedName(c.instance.ObjectMeta)
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ func (r *HPAV2Beta1) New(i interface{}) Columnar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal resource to yaml.
|
// Marshal resource to yaml.
|
||||||
func (r *HPAV2Beta1) Marshal(path string) (string, error) {
|
func (r *HorizontalPodAutoscalerV2Beta1) Marshal(path string) (string, error) {
|
||||||
ns, n := namespaced(path)
|
ns, n := namespaced(path)
|
||||||
i, err := r.Resource.Get(ns, n)
|
i, err := r.Resource.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -66,7 +66,7 @@ func (r *HPAV2Beta1) Marshal(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header return resource header.
|
// Header return resource header.
|
||||||
func (*HPAV2Beta1) Header(ns string) Row {
|
func (*HorizontalPodAutoscalerV2Beta1) Header(ns string) Row {
|
||||||
hh := Row{}
|
hh := Row{}
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
hh = append(hh, "NAMESPACE")
|
hh = append(hh, "NAMESPACE")
|
||||||
|
|
@ -83,7 +83,7 @@ func (*HPAV2Beta1) Header(ns string) Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields retrieves displayable fields.
|
// Fields retrieves displayable fields.
|
||||||
func (r *HPAV2Beta1) Fields(ns string) Row {
|
func (r *HorizontalPodAutoscalerV2Beta1) Fields(ns string) Row {
|
||||||
ff := make(Row, 0, len(r.Header(ns)))
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
|
|
||||||
i := r.instance
|
i := r.instance
|
||||||
|
|
@ -105,7 +105,7 @@ func (r *HPAV2Beta1) Fields(ns string) Row {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func (r *HPAV2Beta1) toMetrics(specs []autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
func (r *HorizontalPodAutoscalerV2Beta1) toMetrics(specs []autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||||
if len(specs) == 0 {
|
if len(specs) == 0 {
|
||||||
return "<none>"
|
return "<none>"
|
||||||
}
|
}
|
||||||
|
|
@ -147,7 +147,7 @@ func (r *HPAV2Beta1) toMetrics(specs []autoscalingv2beta1.MetricSpec, statuses [
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*HPAV2Beta1) externalMetrics(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
func (*HorizontalPodAutoscalerV2Beta1) externalMetrics(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
|
|
||||||
if spec.External.TargetAverageValue != nil {
|
if spec.External.TargetAverageValue != nil {
|
||||||
|
|
@ -163,7 +163,7 @@ func (*HPAV2Beta1) externalMetrics(i int, spec autoscalingv2beta1.MetricSpec, st
|
||||||
return fmt.Sprintf("%s/%s", current, spec.External.TargetValue.String())
|
return fmt.Sprintf("%s/%s", current, spec.External.TargetValue.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*HPAV2Beta1) resourceMetrics(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
func (*HorizontalPodAutoscalerV2Beta1) resourceMetrics(i int, spec autoscalingv2beta1.MetricSpec, statuses []autoscalingv2beta1.MetricStatus) string {
|
||||||
current := "<unknown>"
|
current := "<unknown>"
|
||||||
|
|
||||||
if spec.Resource.TargetAverageValue != nil {
|
if spec.Resource.TargetAverageValue != nil {
|
||||||
|
|
|
||||||
|
|
@ -10,40 +10,40 @@ import (
|
||||||
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
|
autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HPA tracks a kubernetes resource.
|
// HorizontalPodAutoscaler tracks a kubernetes resource.
|
||||||
type HPA struct {
|
type HorizontalPodAutoscaler struct {
|
||||||
*Base
|
*Base
|
||||||
instance *autoscalingv2beta2.HorizontalPodAutoscaler
|
instance *autoscalingv2beta2.HorizontalPodAutoscaler
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPAList returns a new resource list.
|
// NewHorizontalPodAutoscalerList returns a new resource list.
|
||||||
func NewHPAList(c Connection, ns string) List {
|
func NewHorizontalPodAutoscalerList(c Connection, ns string) List {
|
||||||
return NewList(
|
return NewList(
|
||||||
ns,
|
ns,
|
||||||
"hpa",
|
"hpa",
|
||||||
NewHPA(c),
|
NewHorizontalPodAutoscaler(c),
|
||||||
AllVerbsAccess|DescribeAccess,
|
AllVerbsAccess|DescribeAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHPA instantiates a new HPA.
|
// NewHorizontalPodAutoscaler instantiates a new HorizontalPodAutoscaler.
|
||||||
func NewHPA(c Connection) *HPA {
|
func NewHorizontalPodAutoscaler(c Connection) *HorizontalPodAutoscaler {
|
||||||
hpa := &HPA{&Base{Connection: c, Resource: k8s.NewHPAV2Beta2(c)}, nil}
|
hpa := &HorizontalPodAutoscaler{&Base{Connection: c, Resource: k8s.NewHorizontalPodAutoscalerV2Beta2(c)}, nil}
|
||||||
hpa.Factory = hpa
|
hpa.Factory = hpa
|
||||||
|
|
||||||
return hpa
|
return hpa
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new HPA instance from a k8s resource.
|
// New builds a new HorizontalPodAutoscaler instance from a k8s resource.
|
||||||
func (r *HPA) New(i interface{}) Columnar {
|
func (r *HorizontalPodAutoscaler) New(i interface{}) Columnar {
|
||||||
c := NewHPA(r.Connection)
|
c := NewHorizontalPodAutoscaler(r.Connection)
|
||||||
switch instance := i.(type) {
|
switch instance := i.(type) {
|
||||||
case *autoscalingv2beta2.HorizontalPodAutoscaler:
|
case *autoscalingv2beta2.HorizontalPodAutoscaler:
|
||||||
c.instance = instance
|
c.instance = instance
|
||||||
case autoscalingv2beta2.HorizontalPodAutoscaler:
|
case autoscalingv2beta2.HorizontalPodAutoscaler:
|
||||||
c.instance = &instance
|
c.instance = &instance
|
||||||
default:
|
default:
|
||||||
log.Fatal().Msgf("unknown HPA type %#v", i)
|
log.Fatal().Msgf("unknown HorizontalPodAutoscaler type %#v", i)
|
||||||
}
|
}
|
||||||
c.path = c.namespacedName(c.instance.ObjectMeta)
|
c.path = c.namespacedName(c.instance.ObjectMeta)
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ func (r *HPA) New(i interface{}) Columnar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal resource to yaml.
|
// Marshal resource to yaml.
|
||||||
func (r *HPA) Marshal(path string) (string, error) {
|
func (r *HorizontalPodAutoscaler) Marshal(path string) (string, error) {
|
||||||
ns, n := namespaced(path)
|
ns, n := namespaced(path)
|
||||||
i, err := r.Resource.Get(ns, n)
|
i, err := r.Resource.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -66,7 +66,7 @@ func (r *HPA) Marshal(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header return resource header.
|
// Header return resource header.
|
||||||
func (*HPA) Header(ns string) Row {
|
func (*HorizontalPodAutoscaler) Header(ns string) Row {
|
||||||
hh := Row{}
|
hh := Row{}
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
hh = append(hh, "NAMESPACE")
|
hh = append(hh, "NAMESPACE")
|
||||||
|
|
@ -83,7 +83,7 @@ func (*HPA) Header(ns string) Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields retrieves displayable fields.
|
// Fields retrieves displayable fields.
|
||||||
func (r *HPA) Fields(ns string) Row {
|
func (r *HorizontalPodAutoscaler) Fields(ns string) Row {
|
||||||
ff := make(Row, 0, len(r.Header(ns)))
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
|
|
||||||
i := r.instance
|
i := r.instance
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ type (
|
||||||
Reconcile() error
|
Reconcile() error
|
||||||
GetName() string
|
GetName() string
|
||||||
Access(flag int) bool
|
Access(flag int) bool
|
||||||
|
SetFieldSelector(string)
|
||||||
|
SetLabelSelector(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Columnar tracks resources that can be diplayed in a tabular fashion.
|
// Columnar tracks resources that can be diplayed in a tabular fashion.
|
||||||
|
|
@ -84,7 +86,7 @@ type (
|
||||||
// Columnars a collection of columnars.
|
// Columnars a collection of columnars.
|
||||||
Columnars []Columnar
|
Columnars []Columnar
|
||||||
|
|
||||||
// Resource tracks generic Kubernetes resources.
|
// Resource represents a tabular Kubernetes resource.
|
||||||
Resource interface {
|
Resource interface {
|
||||||
New(interface{}) Columnar
|
New(interface{}) Columnar
|
||||||
Get(path string) (Columnar, error)
|
Get(path string) (Columnar, error)
|
||||||
|
|
@ -93,6 +95,8 @@ type (
|
||||||
Describe(kind, pa string, flags *genericclioptions.ConfigFlags) (string, error)
|
Describe(kind, pa string, flags *genericclioptions.ConfigFlags) (string, error)
|
||||||
Marshal(pa string) (string, error)
|
Marshal(pa string) (string, error)
|
||||||
Header(ns string) Row
|
Header(ns string) Row
|
||||||
|
SetFieldSelector(string)
|
||||||
|
SetLabelSelector(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
list struct {
|
list struct {
|
||||||
|
|
@ -118,6 +122,16 @@ func NewList(ns, name string, res Resource, verbs int) *list {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetFieldSelector narrows down resource query given fields selection.
|
||||||
|
func (l *list) SetFieldSelector(s string) {
|
||||||
|
l.resource.SetFieldSelector(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabelSelector narrows down resource query via labels selections.
|
||||||
|
func (l *list) SetLabelSelector(s string) {
|
||||||
|
l.resource.SetLabelSelector(s)
|
||||||
|
}
|
||||||
|
|
||||||
// Access check access control on a given resource.
|
// Access check access control on a given resource.
|
||||||
func (l *list) Access(f int) bool {
|
func (l *list) Access(f int) bool {
|
||||||
return l.verbs&f == f
|
return l.verbs&f == f
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,22 @@ func (mock *MockCruder) List(_param0 string) (k8s.Collection, error) {
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mock *MockCruder) SetFieldSelector(_param0 string) {
|
||||||
|
if mock == nil {
|
||||||
|
panic("mock must not be nil. Use myMock := NewMockCruder().")
|
||||||
|
}
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
pegomock.GetGenericMockFrom(mock).Invoke("SetFieldSelector", params, []reflect.Type{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *MockCruder) SetLabelSelector(_param0 string) {
|
||||||
|
if mock == nil {
|
||||||
|
panic("mock must not be nil. Use myMock := NewMockCruder().")
|
||||||
|
}
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
pegomock.GetGenericMockFrom(mock).Invoke("SetLabelSelector", params, []reflect.Type{})
|
||||||
|
}
|
||||||
|
|
||||||
func (mock *MockCruder) VerifyWasCalledOnce() *VerifierCruder {
|
func (mock *MockCruder) VerifyWasCalledOnce() *VerifierCruder {
|
||||||
return &VerifierCruder{
|
return &VerifierCruder{
|
||||||
mock: mock,
|
mock: mock,
|
||||||
|
|
@ -196,3 +212,57 @@ func (c *Cruder_List_OngoingVerification) GetAllCapturedArguments() (_param0 []s
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (verifier *VerifierCruder) SetFieldSelector(_param0 string) *Cruder_SetFieldSelector_OngoingVerification {
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "SetFieldSelector", params, verifier.timeout)
|
||||||
|
return &Cruder_SetFieldSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cruder_SetFieldSelector_OngoingVerification struct {
|
||||||
|
mock *MockCruder
|
||||||
|
methodInvocations []pegomock.MethodInvocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cruder_SetFieldSelector_OngoingVerification) GetCapturedArguments() string {
|
||||||
|
_param0 := c.GetAllCapturedArguments()
|
||||||
|
return _param0[len(_param0)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cruder_SetFieldSelector_OngoingVerification) GetAllCapturedArguments() (_param0 []string) {
|
||||||
|
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||||
|
if len(params) > 0 {
|
||||||
|
_param0 = make([]string, len(params[0]))
|
||||||
|
for u, param := range params[0] {
|
||||||
|
_param0[u] = param.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (verifier *VerifierCruder) SetLabelSelector(_param0 string) *Cruder_SetLabelSelector_OngoingVerification {
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "SetLabelSelector", params, verifier.timeout)
|
||||||
|
return &Cruder_SetLabelSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cruder_SetLabelSelector_OngoingVerification struct {
|
||||||
|
mock *MockCruder
|
||||||
|
methodInvocations []pegomock.MethodInvocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cruder_SetLabelSelector_OngoingVerification) GetCapturedArguments() string {
|
||||||
|
_param0 := c.GetAllCapturedArguments()
|
||||||
|
return _param0[len(_param0)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cruder_SetLabelSelector_OngoingVerification) GetAllCapturedArguments() (_param0 []string) {
|
||||||
|
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||||
|
if len(params) > 0 {
|
||||||
|
_param0 = make([]string, len(params[0]))
|
||||||
|
for u, param := range params[0] {
|
||||||
|
_param0[u] = param.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,22 @@ func (mock *MockSwitchableCruder) MustCurrentContextName() string {
|
||||||
return ret0
|
return ret0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mock *MockSwitchableCruder) SetFieldSelector(_param0 string) {
|
||||||
|
if mock == nil {
|
||||||
|
panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().")
|
||||||
|
}
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
pegomock.GetGenericMockFrom(mock).Invoke("SetFieldSelector", params, []reflect.Type{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mock *MockSwitchableCruder) SetLabelSelector(_param0 string) {
|
||||||
|
if mock == nil {
|
||||||
|
panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().")
|
||||||
|
}
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
pegomock.GetGenericMockFrom(mock).Invoke("SetLabelSelector", params, []reflect.Type{})
|
||||||
|
}
|
||||||
|
|
||||||
func (mock *MockSwitchableCruder) Switch(_param0 string) error {
|
func (mock *MockSwitchableCruder) Switch(_param0 string) error {
|
||||||
if mock == nil {
|
if mock == nil {
|
||||||
panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().")
|
panic("mock must not be nil. Use myMock := NewMockSwitchableCruder().")
|
||||||
|
|
@ -244,6 +260,60 @@ func (c *SwitchableCruder_MustCurrentContextName_OngoingVerification) GetCapture
|
||||||
func (c *SwitchableCruder_MustCurrentContextName_OngoingVerification) GetAllCapturedArguments() {
|
func (c *SwitchableCruder_MustCurrentContextName_OngoingVerification) GetAllCapturedArguments() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (verifier *VerifierSwitchableCruder) SetFieldSelector(_param0 string) *SwitchableCruder_SetFieldSelector_OngoingVerification {
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "SetFieldSelector", params, verifier.timeout)
|
||||||
|
return &SwitchableCruder_SetFieldSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwitchableCruder_SetFieldSelector_OngoingVerification struct {
|
||||||
|
mock *MockSwitchableCruder
|
||||||
|
methodInvocations []pegomock.MethodInvocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SwitchableCruder_SetFieldSelector_OngoingVerification) GetCapturedArguments() string {
|
||||||
|
_param0 := c.GetAllCapturedArguments()
|
||||||
|
return _param0[len(_param0)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SwitchableCruder_SetFieldSelector_OngoingVerification) GetAllCapturedArguments() (_param0 []string) {
|
||||||
|
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||||
|
if len(params) > 0 {
|
||||||
|
_param0 = make([]string, len(params[0]))
|
||||||
|
for u, param := range params[0] {
|
||||||
|
_param0[u] = param.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (verifier *VerifierSwitchableCruder) SetLabelSelector(_param0 string) *SwitchableCruder_SetLabelSelector_OngoingVerification {
|
||||||
|
params := []pegomock.Param{_param0}
|
||||||
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "SetLabelSelector", params, verifier.timeout)
|
||||||
|
return &SwitchableCruder_SetLabelSelector_OngoingVerification{mock: verifier.mock, methodInvocations: methodInvocations}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwitchableCruder_SetLabelSelector_OngoingVerification struct {
|
||||||
|
mock *MockSwitchableCruder
|
||||||
|
methodInvocations []pegomock.MethodInvocation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SwitchableCruder_SetLabelSelector_OngoingVerification) GetCapturedArguments() string {
|
||||||
|
_param0 := c.GetAllCapturedArguments()
|
||||||
|
return _param0[len(_param0)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SwitchableCruder_SetLabelSelector_OngoingVerification) GetAllCapturedArguments() (_param0 []string) {
|
||||||
|
params := pegomock.GetGenericMockFrom(c.mock).GetInvocationParams(c.methodInvocations)
|
||||||
|
if len(params) > 0 {
|
||||||
|
_param0 = make([]string, len(params[0]))
|
||||||
|
for u, param := range params[0] {
|
||||||
|
_param0[u] = param.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (verifier *VerifierSwitchableCruder) Switch(_param0 string) *SwitchableCruder_Switch_OngoingVerification {
|
func (verifier *VerifierSwitchableCruder) Switch(_param0 string) *SwitchableCruder_Switch_OngoingVerification {
|
||||||
params := []pegomock.Param{_param0}
|
params := []pegomock.Param{_param0}
|
||||||
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Switch", params, verifier.timeout)
|
methodInvocations := pegomock.GetGenericMockFrom(verifier.mock).Verify(verifier.inOrderContext, verifier.invocationCountMatcher, "Switch", params, verifier.timeout)
|
||||||
|
|
|
||||||
|
|
@ -107,14 +107,11 @@ func (*Node) Header(ns string) Row {
|
||||||
return Row{
|
return Row{
|
||||||
"NAME",
|
"NAME",
|
||||||
"STATUS",
|
"STATUS",
|
||||||
|
"ROLE",
|
||||||
"VERSION",
|
"VERSION",
|
||||||
"KERNEL",
|
"KERNEL",
|
||||||
"INTERNAL-IP",
|
"INTERNAL-IP",
|
||||||
"EXTERNAL-IP",
|
"EXTERNAL-IP",
|
||||||
"CPU%",
|
|
||||||
"MEM%",
|
|
||||||
"RCPU%",
|
|
||||||
"RMEM%",
|
|
||||||
"CPU",
|
"CPU",
|
||||||
"MEM",
|
"MEM",
|
||||||
"RCPU",
|
"RCPU",
|
||||||
|
|
@ -143,40 +140,59 @@ func (r *Node) Fields(ns string) Row {
|
||||||
|
|
||||||
rcpu, rmem := reqs["cpu"], reqs["memory"]
|
rcpu, rmem := reqs["cpu"], reqs["memory"]
|
||||||
|
|
||||||
var pcpur float64
|
pcpur := toPerc(float64(rcpu.MilliValue()), float64(r.metrics.AvailCPU))
|
||||||
if r.metrics.AvailCPU > 0 {
|
pmemr := toPerc(k8s.ToMB(rmem.Value()), float64(r.metrics.AvailMEM))
|
||||||
pcpur = toPerc(float64(rcpu.MilliValue()), float64(r.metrics.AvailCPU))
|
|
||||||
}
|
|
||||||
|
|
||||||
var pmemr float64
|
|
||||||
if r.metrics.AvailMEM > 0 {
|
|
||||||
pmemr = toPerc(float64(rmem.Value()/(1024*1024)), float64(r.metrics.AvailMEM))
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(ff,
|
return append(ff,
|
||||||
i.Name,
|
i.Name,
|
||||||
r.status(i),
|
r.status(i),
|
||||||
|
r.nodeRoles(i),
|
||||||
i.Status.NodeInfo.KubeletVersion,
|
i.Status.NodeInfo.KubeletVersion,
|
||||||
i.Status.NodeInfo.KernelVersion,
|
i.Status.NodeInfo.KernelVersion,
|
||||||
iIP,
|
iIP,
|
||||||
eIP,
|
eIP,
|
||||||
asPerc(toPerc(float64(r.metrics.CurrentCPU), float64(r.metrics.AvailCPU))),
|
withPerc(ToMillicore(r.metrics.CurrentCPU), asPerc(toPerc(float64(r.metrics.CurrentCPU), float64(r.metrics.AvailCPU)))),
|
||||||
asPerc(toPerc(r.metrics.CurrentMEM, r.metrics.AvailMEM)),
|
withPerc(ToMi(r.metrics.CurrentMEM), asPerc(toPerc(r.metrics.CurrentMEM, r.metrics.AvailMEM))),
|
||||||
asPerc(pcpur),
|
withPerc(rcpu.String(), asPerc(pcpur)),
|
||||||
asPerc(pmemr),
|
withPerc(rmem.String(), asPerc(pmemr)),
|
||||||
ToMillicore(r.metrics.CurrentCPU),
|
|
||||||
ToMi(r.metrics.CurrentMEM),
|
|
||||||
ToMillicore(r.metrics.AvailCPU),
|
ToMillicore(r.metrics.AvailCPU),
|
||||||
ToMi(r.metrics.AvailMEM),
|
ToMi(r.metrics.AvailMEM),
|
||||||
rcpu.String(),
|
|
||||||
rmem.String(),
|
|
||||||
toAge(i.ObjectMeta.CreationTimestamp),
|
toAge(i.ObjectMeta.CreationTimestamp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func withPerc(v, p string) string {
|
||||||
|
return v + " (" + p + ")"
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
|
func (*Node) nodeRoles(node *v1.Node) string {
|
||||||
|
const (
|
||||||
|
labelNodeRolePrefix = "node-role.kubernetes.io/"
|
||||||
|
nodeLabelRole = "kubernetes.io/role"
|
||||||
|
)
|
||||||
|
|
||||||
|
roles := sets.NewString()
|
||||||
|
for k, v := range node.Labels {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(k, labelNodeRolePrefix):
|
||||||
|
if role := strings.TrimPrefix(k, labelNodeRolePrefix); len(role) > 0 {
|
||||||
|
roles.Insert(role)
|
||||||
|
}
|
||||||
|
|
||||||
|
case k == nodeLabelRole && v != "":
|
||||||
|
roles.Insert(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(roles) == 0 {
|
||||||
|
return MissingValue
|
||||||
|
}
|
||||||
|
return strings.Join(roles.List(), ",")
|
||||||
|
}
|
||||||
|
|
||||||
func (*Node) getIPs(addrs []v1.NodeAddress) (iIP, eIP string) {
|
func (*Node) getIPs(addrs []v1.NodeAddress) (iIP, eIP string) {
|
||||||
for _, a := range addrs {
|
for _, a := range addrs {
|
||||||
switch a.Type {
|
switch a.Type {
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ func TestNodeListData(t *testing.T) {
|
||||||
assert.Equal(t, resource.NotNamespaced, l.GetNamespace())
|
assert.Equal(t, resource.NotNamespaced, l.GetNamespace())
|
||||||
row, ok := td.Rows["fred"]
|
row, ok := td.Rows["fred"]
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
assert.Equal(t, 17, len(row.Deltas))
|
assert.Equal(t, 14, len(row.Deltas))
|
||||||
for _, d := range row.Deltas {
|
for _, d := range row.Deltas {
|
||||||
assert.Equal(t, "", d)
|
assert.Equal(t, "", d)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Container represents a resource that encompass multiple containers.
|
// Containers represents a resource that supports containers.
|
||||||
Container interface {
|
Containers interface {
|
||||||
Containers(path string, includeInit bool) ([]string, error)
|
Containers(path string, includeInit bool) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,40 +9,40 @@ import (
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PV tracks a kubernetes resource.
|
// PersistentVolume tracks a kubernetes resource.
|
||||||
type PV struct {
|
type PersistentVolume struct {
|
||||||
*Base
|
*Base
|
||||||
instance *v1.PersistentVolume
|
instance *v1.PersistentVolume
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPVList returns a new resource list.
|
// NewPersistentVolumeList returns a new resource list.
|
||||||
func NewPVList(c Connection, ns string) List {
|
func NewPersistentVolumeList(c Connection, ns string) List {
|
||||||
return NewList(
|
return NewList(
|
||||||
NotNamespaced,
|
NotNamespaced,
|
||||||
"pv",
|
"pv",
|
||||||
NewPV(c),
|
NewPersistentVolume(c),
|
||||||
CRUDAccess|DescribeAccess,
|
CRUDAccess|DescribeAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPV instantiates a new PV.
|
// NewPersistentVolume instantiates a new PersistentVolume.
|
||||||
func NewPV(c Connection) *PV {
|
func NewPersistentVolume(c Connection) *PersistentVolume {
|
||||||
p := &PV{&Base{Connection: c, Resource: k8s.NewPV(c)}, nil}
|
p := &PersistentVolume{&Base{Connection: c, Resource: k8s.NewPersistentVolume(c)}, nil}
|
||||||
p.Factory = p
|
p.Factory = p
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new PV instance from a k8s resource.
|
// New builds a new PersistentVolume instance from a k8s resource.
|
||||||
func (r *PV) New(i interface{}) Columnar {
|
func (r *PersistentVolume) New(i interface{}) Columnar {
|
||||||
c := NewPV(r.Connection)
|
c := NewPersistentVolume(r.Connection)
|
||||||
switch instance := i.(type) {
|
switch instance := i.(type) {
|
||||||
case *v1.PersistentVolume:
|
case *v1.PersistentVolume:
|
||||||
c.instance = instance
|
c.instance = instance
|
||||||
case v1.PersistentVolume:
|
case v1.PersistentVolume:
|
||||||
c.instance = &instance
|
c.instance = &instance
|
||||||
default:
|
default:
|
||||||
log.Fatal().Msgf("unknown PV type %#v", i)
|
log.Fatal().Msgf("unknown PersistentVolume type %#v", i)
|
||||||
}
|
}
|
||||||
c.path = c.namespacedName(c.instance.ObjectMeta)
|
c.path = c.namespacedName(c.instance.ObjectMeta)
|
||||||
|
|
||||||
|
|
@ -50,7 +50,7 @@ func (r *PV) New(i interface{}) Columnar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal resource to yaml.
|
// Marshal resource to yaml.
|
||||||
func (r *PV) Marshal(path string) (string, error) {
|
func (r *PersistentVolume) Marshal(path string) (string, error) {
|
||||||
ns, n := namespaced(path)
|
ns, n := namespaced(path)
|
||||||
i, err := r.Resource.Get(ns, n)
|
i, err := r.Resource.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -65,7 +65,7 @@ func (r *PV) Marshal(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header return resource header.
|
// Header return resource header.
|
||||||
func (*PV) Header(ns string) Row {
|
func (*PersistentVolume) Header(ns string) Row {
|
||||||
hh := Row{}
|
hh := Row{}
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
hh = append(hh, "NAMESPACE")
|
hh = append(hh, "NAMESPACE")
|
||||||
|
|
@ -75,7 +75,7 @@ func (*PV) Header(ns string) Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields retrieves displayable fields.
|
// Fields retrieves displayable fields.
|
||||||
func (r *PV) Fields(ns string) Row {
|
func (r *PersistentVolume) Fields(ns string) Row {
|
||||||
ff := make(Row, 0, len(r.Header(ns)))
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
i := r.instance
|
i := r.instance
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
|
|
@ -115,7 +115,7 @@ func (r *PV) Fields(ns string) Row {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Helpers...
|
// Helpers...
|
||||||
|
|
||||||
func (r *PV) accessMode(aa []v1.PersistentVolumeAccessMode) string {
|
func (r *PersistentVolume) accessMode(aa []v1.PersistentVolumeAccessMode) string {
|
||||||
dd := r.accessDedup(aa)
|
dd := r.accessDedup(aa)
|
||||||
s := make([]string, 0, len(dd))
|
s := make([]string, 0, len(dd))
|
||||||
for i := 0; i < len(aa); i++ {
|
for i := 0; i < len(aa); i++ {
|
||||||
|
|
@ -132,7 +132,7 @@ func (r *PV) accessMode(aa []v1.PersistentVolumeAccessMode) string {
|
||||||
return strings.Join(s, ",")
|
return strings.Join(s, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PV) accessContains(cc []v1.PersistentVolumeAccessMode, a v1.PersistentVolumeAccessMode) bool {
|
func (r *PersistentVolume) accessContains(cc []v1.PersistentVolumeAccessMode, a v1.PersistentVolumeAccessMode) bool {
|
||||||
for _, c := range cc {
|
for _, c := range cc {
|
||||||
if c == a {
|
if c == a {
|
||||||
return true
|
return true
|
||||||
|
|
@ -142,7 +142,7 @@ func (r *PV) accessContains(cc []v1.PersistentVolumeAccessMode, a v1.PersistentV
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *PV) accessDedup(cc []v1.PersistentVolumeAccessMode) []v1.PersistentVolumeAccessMode {
|
func (r *PersistentVolume) accessDedup(cc []v1.PersistentVolumeAccessMode) []v1.PersistentVolumeAccessMode {
|
||||||
set := []v1.PersistentVolumeAccessMode{}
|
set := []v1.PersistentVolumeAccessMode{}
|
||||||
for _, c := range cc {
|
for _, c := range cc {
|
||||||
if !r.accessContains(set, c) {
|
if !r.accessContains(set, c) {
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,12 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPVListWithArgs(ns string, r *resource.PV) resource.List {
|
func NewPVListWithArgs(ns string, r *resource.PersistentVolume) resource.List {
|
||||||
return resource.NewList(resource.NotNamespaced, "pv", r, resource.CRUDAccess|resource.DescribeAccess)
|
return resource.NewList(resource.NotNamespaced, "pv", r, resource.CRUDAccess|resource.DescribeAccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPVWithArgs(conn k8s.Connection, res resource.Cruder) *resource.PV {
|
func NewPVWithArgs(conn k8s.Connection, res resource.Cruder) *resource.PersistentVolume {
|
||||||
r := &resource.PV{Base: resource.NewBase(conn, res)}
|
r := &resource.PersistentVolume{Base: resource.NewBase(conn, res)}
|
||||||
r.Factory = r
|
r.Factory = r
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
@ -92,7 +92,7 @@ func k8sPV() *v1.PersistentVolume {
|
||||||
|
|
||||||
func newPV() resource.Columnar {
|
func newPV() resource.Columnar {
|
||||||
mc := NewMockConnection()
|
mc := NewMockConnection()
|
||||||
return resource.NewPV(mc).New(k8sPV())
|
return resource.NewPersistentVolume(mc).New(k8sPV())
|
||||||
}
|
}
|
||||||
|
|
||||||
func pvYaml() string {
|
func pvYaml() string {
|
||||||
|
|
|
||||||
|
|
@ -6,40 +6,40 @@ import (
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PVC tracks a kubernetes resource.
|
// PersistentVolumeClaim tracks a kubernetes resource.
|
||||||
type PVC struct {
|
type PersistentVolumeClaim struct {
|
||||||
*Base
|
*Base
|
||||||
instance *v1.PersistentVolumeClaim
|
instance *v1.PersistentVolumeClaim
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPVCList returns a new resource list.
|
// NewPersistentVolumeClaimList returns a new resource list.
|
||||||
func NewPVCList(c Connection, ns string) List {
|
func NewPersistentVolumeClaimList(c Connection, ns string) List {
|
||||||
return NewList(
|
return NewList(
|
||||||
ns,
|
ns,
|
||||||
"pvc",
|
"pvc",
|
||||||
NewPVC(c),
|
NewPersistentVolumeClaim(c),
|
||||||
AllVerbsAccess|DescribeAccess,
|
AllVerbsAccess|DescribeAccess,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPVC instantiates a new PVC.
|
// NewPersistentVolumeClaim instantiates a new PersistentVolumeClaim.
|
||||||
func NewPVC(c Connection) *PVC {
|
func NewPersistentVolumeClaim(c Connection) *PersistentVolumeClaim {
|
||||||
p := &PVC{&Base{Connection: c, Resource: k8s.NewPVC(c)}, nil}
|
p := &PersistentVolumeClaim{&Base{Connection: c, Resource: k8s.NewPersistentVolumeClaim(c)}, nil}
|
||||||
p.Factory = p
|
p.Factory = p
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// New builds a new PVC instance from a k8s resource.
|
// New builds a new PersistentVolumeClaim instance from a k8s resource.
|
||||||
func (r *PVC) New(i interface{}) Columnar {
|
func (r *PersistentVolumeClaim) New(i interface{}) Columnar {
|
||||||
c := NewPVC(r.Connection)
|
c := NewPersistentVolumeClaim(r.Connection)
|
||||||
switch instance := i.(type) {
|
switch instance := i.(type) {
|
||||||
case *v1.PersistentVolumeClaim:
|
case *v1.PersistentVolumeClaim:
|
||||||
c.instance = instance
|
c.instance = instance
|
||||||
case v1.PersistentVolumeClaim:
|
case v1.PersistentVolumeClaim:
|
||||||
c.instance = &instance
|
c.instance = &instance
|
||||||
default:
|
default:
|
||||||
log.Fatal().Msgf("unknown PVC type %#v", i)
|
log.Fatal().Msgf("unknown PersistentVolumeClaim type %#v", i)
|
||||||
}
|
}
|
||||||
c.path = c.namespacedName(c.instance.ObjectMeta)
|
c.path = c.namespacedName(c.instance.ObjectMeta)
|
||||||
|
|
||||||
|
|
@ -47,7 +47,7 @@ func (r *PVC) New(i interface{}) Columnar {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Marshal resource to yaml.
|
// Marshal resource to yaml.
|
||||||
func (r *PVC) Marshal(path string) (string, error) {
|
func (r *PersistentVolumeClaim) Marshal(path string) (string, error) {
|
||||||
ns, n := namespaced(path)
|
ns, n := namespaced(path)
|
||||||
i, err := r.Resource.Get(ns, n)
|
i, err := r.Resource.Get(ns, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -62,7 +62,7 @@ func (r *PVC) Marshal(path string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header return resource header.
|
// Header return resource header.
|
||||||
func (*PVC) Header(ns string) Row {
|
func (*PersistentVolumeClaim) Header(ns string) Row {
|
||||||
hh := Row{}
|
hh := Row{}
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
hh = append(hh, "NAMESPACE")
|
hh = append(hh, "NAMESPACE")
|
||||||
|
|
@ -72,7 +72,7 @@ func (*PVC) Header(ns string) Row {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields retrieves displayable fields.
|
// Fields retrieves displayable fields.
|
||||||
func (r *PVC) Fields(ns string) Row {
|
func (r *PersistentVolumeClaim) Fields(ns string) Row {
|
||||||
ff := make(Row, 0, len(r.Header(ns)))
|
ff := make(Row, 0, len(r.Header(ns)))
|
||||||
i := r.instance
|
i := r.instance
|
||||||
if ns == AllNamespaces {
|
if ns == AllNamespaces {
|
||||||
|
|
@ -84,7 +84,7 @@ func (r *PVC) Fields(ns string) Row {
|
||||||
phase = "Terminating"
|
phase = "Terminating"
|
||||||
}
|
}
|
||||||
|
|
||||||
pv := PV{}
|
var pv PersistentVolume
|
||||||
storage := i.Spec.Resources.Requests[v1.ResourceStorage]
|
storage := i.Spec.Resources.Requests[v1.ResourceStorage]
|
||||||
var capacity, accessModes string
|
var capacity, accessModes string
|
||||||
if i.Spec.VolumeName != "" {
|
if i.Spec.VolumeName != "" {
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@ import (
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPVCListWithArgs(ns string, r *resource.PVC) resource.List {
|
func NewPVCListWithArgs(ns string, r *resource.PersistentVolumeClaim) resource.List {
|
||||||
return resource.NewList(ns, "pvc", r, resource.AllVerbsAccess|resource.DescribeAccess)
|
return resource.NewList(ns, "pvc", r, resource.AllVerbsAccess|resource.DescribeAccess)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPVCWithArgs(conn k8s.Connection, res resource.Cruder) *resource.PVC {
|
func NewPVCWithArgs(conn k8s.Connection, res resource.Cruder) *resource.PersistentVolumeClaim {
|
||||||
r := &resource.PVC{Base: resource.NewBase(conn, res)}
|
r := &resource.PersistentVolumeClaim{Base: resource.NewBase(conn, res)}
|
||||||
r.Factory = r
|
r.Factory = r
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +100,7 @@ func k8sPVC() *v1.PersistentVolumeClaim {
|
||||||
|
|
||||||
func newPVC() resource.Columnar {
|
func newPVC() resource.Columnar {
|
||||||
mc := NewMockConnection()
|
mc := NewMockConnection()
|
||||||
return resource.NewPVC(mc).New(k8sPVC())
|
return resource.NewPersistentVolumeClaim(mc).New(k8sPVC())
|
||||||
}
|
}
|
||||||
|
|
||||||
func pvcYaml() string {
|
func pvcYaml() string {
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ func (a *appView) showPage(p string) {
|
||||||
a.pages.SwitchToPage(p)
|
a.pages.SwitchToPage(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *appView) inject(p igniter) {
|
func (a *appView) inject(i igniter) {
|
||||||
if a.cancel != nil {
|
if a.cancel != nil {
|
||||||
a.cancel()
|
a.cancel()
|
||||||
}
|
}
|
||||||
|
|
@ -273,14 +273,14 @@ func (a *appView) inject(p igniter) {
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
{
|
{
|
||||||
ctx, a.cancel = context.WithCancel(context.Background())
|
ctx, a.cancel = context.WithCancel(context.Background())
|
||||||
p.init(ctx, a.config.ActiveNamespace())
|
i.init(ctx, a.config.ActiveNamespace())
|
||||||
}
|
}
|
||||||
a.content.AddPage("main", p, true, true)
|
a.content.AddPage("main", i, true, true)
|
||||||
|
|
||||||
a.focusGroup = append([]tview.Primitive{}, p)
|
a.focusGroup = append([]tview.Primitive{}, i)
|
||||||
a.focusCurrent = 0
|
a.focusCurrent = 0
|
||||||
a.fireFocusChanged(p)
|
a.fireFocusChanged(i)
|
||||||
a.SetFocus(p)
|
a.SetFocus(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *appView) cmdMode() bool {
|
func (a *appView) cmdMode() bool {
|
||||||
|
|
|
||||||
|
|
@ -62,9 +62,6 @@ func (c *command) run(cmd string) bool {
|
||||||
case cmd == "alias":
|
case cmd == "alias":
|
||||||
c.app.inject(newAliasView(c.app))
|
c.app.inject(newAliasView(c.app))
|
||||||
return true
|
return true
|
||||||
case cmd == "popeye":
|
|
||||||
c.app.inject(newPopeyeView(c.app))
|
|
||||||
return true
|
|
||||||
case policyMatcher.MatchString(cmd):
|
case policyMatcher.MatchString(cmd):
|
||||||
tokens := policyMatcher.FindAllStringSubmatch(cmd, -1)
|
tokens := policyMatcher.FindAllStringSubmatch(cmd, -1)
|
||||||
if len(tokens) == 1 && len(tokens[0]) == 3 {
|
if len(tokens) == 1 && len(tokens[0]) == 3 {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/derailed/k9s/internal/resource"
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type containerView struct {
|
||||||
|
*resourceView
|
||||||
|
|
||||||
|
current igniter
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContainerView(t string, app *appView, list resource.List, path string) resourceViewer {
|
||||||
|
v := containerView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
||||||
|
{
|
||||||
|
v.path = path
|
||||||
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.current = app.content.GetPrimitive("main").(igniter)
|
||||||
|
}
|
||||||
|
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
|
||||||
|
v.switchPage("co")
|
||||||
|
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protocol...
|
||||||
|
|
||||||
|
func (v *containerView) backFn() actionHandler {
|
||||||
|
return v.backCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) appView() *appView {
|
||||||
|
return v.app
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) getList() resource.List {
|
||||||
|
return v.list
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) getSelection() string {
|
||||||
|
return v.path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handlers...
|
||||||
|
|
||||||
|
func (v *containerView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if !v.rowSelected() {
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
v.showLogs(v.selectedItem, v.list.GetName(), v, false)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) prevLogsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if !v.rowSelected() {
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
v.showLogs(v.selectedItem, v.list.GetName(), v, true)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) showLogs(co, view string, parent loggable, prev bool) {
|
||||||
|
l := v.GetPrimitive("logs").(*logsView)
|
||||||
|
l.reload(co, parent, view, prev)
|
||||||
|
v.switchPage("logs")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if !v.rowSelected() {
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
log.Debug().Msgf("Selected %s", v.selectedItem)
|
||||||
|
|
||||||
|
v.shellIn(v.path, v.selectedItem)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) shellIn(path, co string) {
|
||||||
|
ns, po := namespaced(path)
|
||||||
|
args := make([]string, 0, 12)
|
||||||
|
args = append(args, "exec", "-it")
|
||||||
|
args = append(args, "--context", v.app.config.K9s.CurrentContext)
|
||||||
|
args = append(args, "-n", ns)
|
||||||
|
args = append(args, po)
|
||||||
|
if len(co) != 0 {
|
||||||
|
args = append(args, "-c", co)
|
||||||
|
}
|
||||||
|
args = append(args, "--", "sh")
|
||||||
|
log.Debug().Msgf("Shell args %v", args)
|
||||||
|
runK(v.app, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) extraActions(aa keyActions) {
|
||||||
|
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
||||||
|
aa[KeyShiftL] = newKeyAction("Previous Logs", v.prevLogsCmd, true)
|
||||||
|
aa[KeyS] = newKeyAction("Shell", v.shellCmd, true)
|
||||||
|
aa[tcell.KeyEscape] = newKeyAction("Back", v.backCmd, false)
|
||||||
|
aa[KeyP] = newKeyAction("Previous", v.backCmd, false)
|
||||||
|
aa[tcell.KeyEnter] = newKeyAction("Enter", v.logsCmd, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
return func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
t := v.getTV()
|
||||||
|
t.sortCol.index, t.sortCol.asc = t.nameColIndex()+col, asc
|
||||||
|
t.refresh()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *containerView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
v.app.inject(v.current)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -70,6 +70,7 @@ func (v *helpView) init(_ context.Context, _ string) {
|
||||||
{"tab", "Next term match"},
|
{"tab", "Next term match"},
|
||||||
{"backtab", "Previous term match"},
|
{"backtab", "Previous term match"},
|
||||||
{"Ctrl-r", "Refresh"},
|
{"Ctrl-r", "Refresh"},
|
||||||
|
{"Shift-i", "Invert Sort"},
|
||||||
{"p", "Previous resource view"},
|
{"p", "Previous resource view"},
|
||||||
{"q", "Quit"},
|
{"q", "Quit"},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ type jobView struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newJobView(t string, app *appView, list resource.List) resourceViewer {
|
func newJobView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := jobView{newResourceView(t, app, list).(*resourceView)}
|
v := jobView{resourceView: newResourceView(t, app, list).(*resourceView)}
|
||||||
{
|
{
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
|
v.AddPage("logs", newLogsView(list.GetName(), &v), true, false)
|
||||||
|
|
@ -50,42 +50,53 @@ func (v *jobView) getSelection() string {
|
||||||
// Handlers...
|
// Handlers...
|
||||||
|
|
||||||
func (v *jobView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *jobView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if v.viewLogs(false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *jobView) prevLogsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if v.viewLogs(true) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *jobView) viewLogs(previous bool) bool {
|
||||||
if !v.rowSelected() {
|
if !v.rowSelected() {
|
||||||
return evt
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, err := fetchContainers(v.list, v.selectedItem, true)
|
cc, err := fetchContainers(v.list, v.selectedItem, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.app.flash(flashErr, err.Error())
|
v.app.flash(flashErr, err.Error())
|
||||||
log.Error().Err(err).Msgf("Unable to fetch containers for %s", v.selectedItem)
|
log.Error().Err(err).Msgf("Unable to fetch containers for %s", v.selectedItem)
|
||||||
return evt
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cc) == 1 {
|
if len(cc) == 1 {
|
||||||
v.showLogs(v.selectedItem, cc[0], v.list.GetName(), v)
|
v.showLogs(v.selectedItem, cc[0], v.list.GetName(), v, previous)
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
picker := v.GetPrimitive("picker").(*selectList)
|
picker := v.GetPrimitive("picker").(*selectList)
|
||||||
picker.populate(cc)
|
picker.populate(cc)
|
||||||
picker.SetSelectedFunc(func(i int, t, d string, r rune) {
|
picker.SetSelectedFunc(func(i int, t, d string, r rune) {
|
||||||
v.showLogs(v.selectedItem, t, "picker", picker)
|
v.showLogs(v.selectedItem, t, "picker", picker, previous)
|
||||||
})
|
})
|
||||||
v.switchPage("picker")
|
v.switchPage("picker")
|
||||||
|
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *jobView) showLogs(path, co, view string, parent loggable) {
|
func (v *jobView) showLogs(path, co, view string, parent loggable, prev bool) {
|
||||||
l := v.GetPrimitive("logs").(*logsView)
|
l := v.GetPrimitive("logs").(*logsView)
|
||||||
l.parent = parent
|
l.reload(co, parent, view, prev)
|
||||||
l.parentView = view
|
|
||||||
l.deleteAllPages()
|
|
||||||
l.addContainer(co)
|
|
||||||
v.switchPage("logs")
|
v.switchPage("logs")
|
||||||
l.init()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *jobView) extraActions(aa keyActions) {
|
func (v *jobView) extraActions(aa keyActions) {
|
||||||
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
||||||
|
aa[KeyShiftL] = newKeyAction("Previous Logs", v.prevLogsCmd, true)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,13 @@ const (
|
||||||
type logsView struct {
|
type logsView struct {
|
||||||
*tview.Pages
|
*tview.Pages
|
||||||
|
|
||||||
parentView string
|
parentView string
|
||||||
parent loggable
|
parent loggable
|
||||||
containers []string
|
containers []string
|
||||||
actions keyActions
|
actions keyActions
|
||||||
cancelFunc context.CancelFunc
|
cancelFunc context.CancelFunc
|
||||||
autoScroll bool
|
autoScroll bool
|
||||||
|
showPrevious bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newLogsView(pview string, parent loggable) *logsView {
|
func newLogsView(pview string, parent loggable) *logsView {
|
||||||
|
|
@ -53,7 +54,10 @@ func newLogsView(pview string, parent loggable) *logsView {
|
||||||
|
|
||||||
// Protocol...
|
// Protocol...
|
||||||
|
|
||||||
func (v *logsView) init() {
|
func (v *logsView) reload(co string, parent loggable, view string, prevLogs bool) {
|
||||||
|
v.parent, v.parentView, v.showPrevious = parent, view, prevLogs
|
||||||
|
v.deleteAllPages()
|
||||||
|
v.addContainer(co)
|
||||||
v.load(0)
|
v.load(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +175,7 @@ func (v *logsView) doLoad(path, co string) error {
|
||||||
return fmt.Errorf("Resource %T is not tailable", v.parent.getList().Resource)
|
return fmt.Errorf("Resource %T is not tailable", v.parent.getList().Resource)
|
||||||
}
|
}
|
||||||
maxBuff := int64(v.parent.appView().config.K9s.LogRequestSize)
|
maxBuff := int64(v.parent.appView().config.K9s.LogRequestSize)
|
||||||
cancelFn, err := res.Logs(c, ns, po, co, maxBuff, false)
|
cancelFn, err := res.Logs(c, ns, po, co, maxBuff, v.showPrevious)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cancelFn()
|
cancelFn()
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,8 @@ func newNodeView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *nodeView) extraActions(aa keyActions) {
|
func (v *nodeView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftQ] = newKeyAction("Sort CPU%", v.sortColCmd(6, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(7, false), true)
|
||||||
aa[KeyShiftW] = newKeyAction("Sort MEM%", v.sortColCmd(7, false), true)
|
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(8, false), true)
|
||||||
aa[KeyShiftA] = newKeyAction("Sort RCPU%", v.sortColCmd(8, false), true)
|
|
||||||
aa[KeyShiftS] = newKeyAction("Sort RMEM%", v.sortColCmd(9, false), true)
|
|
||||||
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(10, false), true)
|
|
||||||
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(11, false), true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *nodeView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *nodeView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/derailed/k9s/internal/k8s"
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const fmat = "[aqua::b]%s([fuchsia::b]%s[aqua::-])"
|
||||||
|
|
||||||
type podView struct {
|
type podView struct {
|
||||||
*resourceView
|
*resourceView
|
||||||
}
|
}
|
||||||
|
|
@ -22,6 +28,7 @@ func newPodView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
v := podView{newResourceView(t, app, list).(*resourceView)}
|
v := podView{newResourceView(t, app, list).(*resourceView)}
|
||||||
{
|
{
|
||||||
v.extraActionsFn = v.extraActions
|
v.extraActionsFn = v.extraActions
|
||||||
|
v.enterFn = v.listContainers
|
||||||
}
|
}
|
||||||
|
|
||||||
picker := newSelectList(&v)
|
picker := newSelectList(&v)
|
||||||
|
|
@ -38,6 +45,20 @@ func newPodView(t string, app *appView, list resource.List) resourceViewer {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *podView) listContainers(app *appView, _, res, sel string) {
|
||||||
|
ns, n := namespaced(sel)
|
||||||
|
po, err := app.conn().DialOrDie().CoreV1().Pods(ns).Get(n, metav1.GetOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msgf("Unable to retrieve pod %s", sel)
|
||||||
|
app.flash(flashErr, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mx := k8s.NewMetricsServer(app.conn())
|
||||||
|
list := resource.NewContainerList(app.conn(), mx, po)
|
||||||
|
app.inject(newContainerView(fmt.Sprintf(fmat, "Containers", sel), app, list, namespacedName(po.Namespace, po.Name)))
|
||||||
|
}
|
||||||
|
|
||||||
// Protocol...
|
// Protocol...
|
||||||
|
|
||||||
func (v *podView) backFn() actionHandler {
|
func (v *podView) backFn() actionHandler {
|
||||||
|
|
@ -59,40 +80,51 @@ func (v *podView) getSelection() string {
|
||||||
// Handlers...
|
// Handlers...
|
||||||
|
|
||||||
func (v *podView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *podView) logsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if v.viewLogs(false) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *podView) prevLogsCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
if v.viewLogs(true) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return evt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *podView) viewLogs(prev bool) bool {
|
||||||
if !v.rowSelected() {
|
if !v.rowSelected() {
|
||||||
return evt
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
cc, err := fetchContainers(v.list, v.selectedItem, true)
|
cc, err := fetchContainers(v.list, v.selectedItem, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.app.flash(flashErr, err.Error())
|
v.app.flash(flashErr, err.Error())
|
||||||
log.Error().Err(err)
|
log.Error().Err(err)
|
||||||
return evt
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(cc) == 1 {
|
if len(cc) == 1 {
|
||||||
v.showLogs(v.selectedItem, cc[0], v.list.GetName(), v)
|
v.showLogs(v.selectedItem, cc[0], v.list.GetName(), v, prev)
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
picker := v.GetPrimitive("picker").(*selectList)
|
picker := v.GetPrimitive("picker").(*selectList)
|
||||||
picker.populate(cc)
|
picker.populate(cc)
|
||||||
picker.SetSelectedFunc(func(i int, t, d string, r rune) {
|
picker.SetSelectedFunc(func(i int, t, d string, r rune) {
|
||||||
v.showLogs(v.selectedItem, t, "picker", picker)
|
v.showLogs(v.selectedItem, t, "picker", picker, prev)
|
||||||
})
|
})
|
||||||
v.switchPage("picker")
|
v.switchPage("picker")
|
||||||
|
|
||||||
return nil
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *podView) showLogs(path, co, view string, parent loggable) {
|
func (v *podView) showLogs(path, co, view string, parent loggable, prev bool) {
|
||||||
l := v.GetPrimitive("logs").(*logsView)
|
l := v.GetPrimitive("logs").(*logsView)
|
||||||
l.parent = parent
|
l.reload(co, parent, view, prev)
|
||||||
l.parentView = view
|
|
||||||
l.deleteAllPages()
|
|
||||||
l.addContainer(co)
|
|
||||||
v.switchPage("logs")
|
v.switchPage("logs")
|
||||||
l.init()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *podView) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *podView) shellCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -139,6 +171,7 @@ func (v *podView) shellIn(path, co string) {
|
||||||
|
|
||||||
func (v *podView) extraActions(aa keyActions) {
|
func (v *podView) extraActions(aa keyActions) {
|
||||||
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
aa[KeyL] = newKeyAction("Logs", v.logsCmd, true)
|
||||||
|
aa[KeyShiftL] = newKeyAction("Logs", v.prevLogsCmd, true)
|
||||||
aa[KeyS] = newKeyAction("Shell", v.shellCmd, true)
|
aa[KeyS] = newKeyAction("Shell", v.shellCmd, true)
|
||||||
aa[KeyShiftR] = newKeyAction("Sort Ready", v.sortColCmd(1, false), true)
|
aa[KeyShiftR] = newKeyAction("Sort Ready", v.sortColCmd(1, false), true)
|
||||||
aa[KeyShiftS] = newKeyAction("Sort Status", v.sortColCmd(2, true), true)
|
aa[KeyShiftS] = newKeyAction("Sort Status", v.sortColCmd(2, true), true)
|
||||||
|
|
@ -146,7 +179,6 @@ func (v *podView) extraActions(aa keyActions) {
|
||||||
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(4, false), true)
|
aa[KeyShiftC] = newKeyAction("Sort CPU", v.sortColCmd(4, false), true)
|
||||||
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(5, false), true)
|
aa[KeyShiftM] = newKeyAction("Sort MEM", v.sortColCmd(5, false), true)
|
||||||
aa[KeyShiftO] = newKeyAction("Sort Node", v.sortColCmd(7, true), true)
|
aa[KeyShiftO] = newKeyAction("Sort Node", v.sortColCmd(7, true), true)
|
||||||
aa[KeyShiftQ] = newKeyAction("Sort QOS", v.sortColCmd(8, true), true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *podView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
func (v *podView) sortColCmd(col int, asc bool) func(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|
@ -163,5 +195,5 @@ func fetchContainers(l resource.List, po string, includeInit bool) ([]string, er
|
||||||
if len(po) == 0 {
|
if len(po) == 0 {
|
||||||
return []string{}, nil
|
return []string{}, nil
|
||||||
}
|
}
|
||||||
return l.Resource().(resource.Container).Containers(po, includeInit)
|
return l.Resource().(resource.Containers).Containers(po, includeInit)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,77 +1,77 @@
|
||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"context"
|
// "context"
|
||||||
"fmt"
|
// "fmt"
|
||||||
"io"
|
// "io"
|
||||||
"os"
|
// "os"
|
||||||
"path/filepath"
|
// "path/filepath"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/config"
|
// "github.com/derailed/k9s/internal/config"
|
||||||
"github.com/derailed/popeye/pkg"
|
// "github.com/derailed/popeye/pkg"
|
||||||
cfg "github.com/derailed/popeye/pkg/config"
|
// cfg "github.com/derailed/popeye/pkg/config"
|
||||||
"github.com/derailed/tview"
|
// "github.com/derailed/tview"
|
||||||
"github.com/gdamore/tcell"
|
// "github.com/gdamore/tcell"
|
||||||
"github.com/rs/zerolog/log"
|
// "github.com/rs/zerolog/log"
|
||||||
)
|
// )
|
||||||
|
|
||||||
type popeyeView struct {
|
// type popeyeView struct {
|
||||||
*detailsView
|
// *detailsView
|
||||||
|
|
||||||
current igniter
|
// current igniter
|
||||||
ansiWriter io.Writer
|
// ansiWriter io.Writer
|
||||||
}
|
// }
|
||||||
|
|
||||||
func newPopeyeView(app *appView) *popeyeView {
|
// func newPopeyeView(app *appView) *popeyeView {
|
||||||
v := popeyeView{}
|
// v := popeyeView{}
|
||||||
{
|
// {
|
||||||
v.detailsView = newDetailsView(app, v.backCmd)
|
// v.detailsView = newDetailsView(app, v.backCmd)
|
||||||
v.SetBorderPadding(0, 0, 1, 1)
|
// v.SetBorderPadding(0, 0, 1, 1)
|
||||||
v.current = app.content.GetPrimitive("main").(igniter)
|
// v.current = app.content.GetPrimitive("main").(igniter)
|
||||||
v.SetDynamicColors(true)
|
// v.SetDynamicColors(true)
|
||||||
v.SetWrap(true)
|
// v.SetWrap(true)
|
||||||
v.setTitle("Popeye")
|
// v.setTitle("Popeye")
|
||||||
v.ansiWriter = tview.ANSIWriter(v)
|
// v.ansiWriter = tview.ANSIWriter(v)
|
||||||
}
|
// }
|
||||||
v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
|
// v.actions[KeyP] = newKeyAction("Previous", v.app.prevCmd, false)
|
||||||
|
|
||||||
return &v
|
// return &v
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (v *popeyeView) init(ctx context.Context, ns string) {
|
// func (v *popeyeView) init(ctx context.Context, ns string) {
|
||||||
defer func() {
|
// defer func() {
|
||||||
if err := recover(); err != nil {
|
// if err := recover(); err != nil {
|
||||||
v.app.flash(flashErr, fmt.Sprintf("%v", err))
|
// v.app.flash(flashErr, fmt.Sprintf("%v", err))
|
||||||
}
|
// }
|
||||||
}()
|
// }()
|
||||||
|
|
||||||
c := cfg.New()
|
// c := cfg.New()
|
||||||
|
|
||||||
spinach := filepath.Join(config.K9sHome, "spinach.yml")
|
// spinach := filepath.Join(config.K9sHome, "spinach.yml")
|
||||||
|
|
||||||
if _, err := os.Stat(spinach); err == nil {
|
// if _, err := os.Stat(spinach); err == nil {
|
||||||
c.Spinach = spinach
|
// c.Spinach = spinach
|
||||||
}
|
// }
|
||||||
|
|
||||||
if v.app.config.K9s.CurrentContext != "" {
|
// if v.app.config.K9s.CurrentContext != "" {
|
||||||
v.app.flags.Context = &v.app.config.K9s.CurrentContext
|
// v.app.flags.Context = &v.app.config.K9s.CurrentContext
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := c.Init(v.app.flags); err != nil {
|
// if err := c.Init(v.app.flags); err != nil {
|
||||||
log.Error().Err(err).Msg("Unable to load spinach config")
|
// log.Error().Err(err).Msg("Unable to load spinach config")
|
||||||
}
|
// }
|
||||||
|
|
||||||
p := pkg.NewPopeye(c, &log.Logger, v.ansiWriter)
|
// p := pkg.NewPopeye(c, &log.Logger, v.ansiWriter)
|
||||||
p.Sanitize(false)
|
// p.Sanitize(false)
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (v *popeyeView) getTitle() string {
|
// func (v *popeyeView) getTitle() string {
|
||||||
return "Popeye"
|
// return "Popeye"
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (v *popeyeView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
// func (v *popeyeView) backCmd(evt *tcell.EventKey) *tcell.EventKey {
|
||||||
v.app.command.previousCmd()
|
// v.app.command.previousCmd()
|
||||||
v.app.inject(v.current)
|
// v.app.inject(v.current)
|
||||||
|
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
|
||||||
|
|
@ -44,8 +44,7 @@ func helpCmds(c k8s.Connection) map[string]resCmd {
|
||||||
func allCRDs(c k8s.Connection) map[string]k8s.APIGroup {
|
func allCRDs(c k8s.Connection) map[string]k8s.APIGroup {
|
||||||
m := map[string]k8s.APIGroup{}
|
m := map[string]k8s.APIGroup{}
|
||||||
|
|
||||||
crds, _ := resource.
|
crds, _ := resource.NewCustomResourceDefinitionList(c, resource.AllNamespaces).
|
||||||
NewCRDList(c, resource.AllNamespaces).
|
|
||||||
Resource().
|
Resource().
|
||||||
List(resource.AllNamespaces)
|
List(resource.AllNamespaces)
|
||||||
|
|
||||||
|
|
@ -136,7 +135,7 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
||||||
title: "CustomResourceDefinitions",
|
title: "CustomResourceDefinitions",
|
||||||
api: "apiextensions.k8s.io",
|
api: "apiextensions.k8s.io",
|
||||||
viewFn: newResourceView,
|
viewFn: newResourceView,
|
||||||
listFn: resource.NewCRDList,
|
listFn: resource.NewCustomResourceDefinitionList,
|
||||||
},
|
},
|
||||||
"cj": {
|
"cj": {
|
||||||
title: "CronJobs",
|
title: "CronJobs",
|
||||||
|
|
@ -222,14 +221,14 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
||||||
title: "PersistentVolumes",
|
title: "PersistentVolumes",
|
||||||
api: "",
|
api: "",
|
||||||
viewFn: newResourceView,
|
viewFn: newResourceView,
|
||||||
listFn: resource.NewPVList,
|
listFn: resource.NewPersistentVolumeList,
|
||||||
colorerFn: pvColorer,
|
colorerFn: pvColorer,
|
||||||
},
|
},
|
||||||
"pvc": {
|
"pvc": {
|
||||||
title: "PersistentVolumeClaims",
|
title: "PersistentVolumeClaims",
|
||||||
api: "",
|
api: "",
|
||||||
viewFn: newResourceView,
|
viewFn: newResourceView,
|
||||||
listFn: resource.NewPVCList,
|
listFn: resource.NewPersistentVolumeClaimList,
|
||||||
colorerFn: pvcColorer,
|
colorerFn: pvcColorer,
|
||||||
},
|
},
|
||||||
"rb": {
|
"rb": {
|
||||||
|
|
@ -310,7 +309,7 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
||||||
title: "HorizontalPodAutoscalers",
|
title: "HorizontalPodAutoscalers",
|
||||||
api: "autoscaling",
|
api: "autoscaling",
|
||||||
viewFn: newResourceView,
|
viewFn: newResourceView,
|
||||||
listFn: resource.NewHPAV1List,
|
listFn: resource.NewHorizontalPodAutoscalerV1List,
|
||||||
}
|
}
|
||||||
case "v2beta1":
|
case "v2beta1":
|
||||||
log.Debug().Msg("Using HPA V2Beta1!")
|
log.Debug().Msg("Using HPA V2Beta1!")
|
||||||
|
|
@ -318,7 +317,7 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
||||||
title: "HorizontalPodAutoscalers",
|
title: "HorizontalPodAutoscalers",
|
||||||
api: "autoscaling",
|
api: "autoscaling",
|
||||||
viewFn: newResourceView,
|
viewFn: newResourceView,
|
||||||
listFn: resource.NewHPAV2Beta1List,
|
listFn: resource.NewHorizontalPodAutoscalerV2Beta1List,
|
||||||
}
|
}
|
||||||
case "v2beta2":
|
case "v2beta2":
|
||||||
log.Debug().Msg("Using HPA V2Beta2!")
|
log.Debug().Msg("Using HPA V2Beta2!")
|
||||||
|
|
@ -326,7 +325,7 @@ func resourceViews(c k8s.Connection) map[string]resCmd {
|
||||||
title: "HorizontalPodAutoscalers",
|
title: "HorizontalPodAutoscalers",
|
||||||
api: "autoscaling",
|
api: "autoscaling",
|
||||||
viewFn: newResourceView,
|
viewFn: newResourceView,
|
||||||
listFn: resource.NewHPAList,
|
listFn: resource.NewHorizontalPodAutoscalerList,
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Panic().Msgf("K9s does not currently support HPA version `%s`", rev)
|
log.Panic().Msgf("K9s does not currently support HPA version `%s`", rev)
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@ package views
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derailed/k9s/internal/resource"
|
"github.com/derailed/k9s/internal/resource"
|
||||||
res "k8s.io/apimachinery/pkg/api/resource"
|
res "k8s.io/apimachinery/pkg/api/resource"
|
||||||
|
"vbom.ml/util/sortorder"
|
||||||
)
|
)
|
||||||
|
|
||||||
type rowSorter struct {
|
type rowSorter struct {
|
||||||
|
|
@ -63,11 +63,11 @@ func less(asc bool, c1, c2 string) bool {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
c := strings.Compare(c1, c2)
|
b := sortorder.NaturalLess(c1, c2)
|
||||||
if asc {
|
if asc {
|
||||||
return c < 0
|
return b
|
||||||
}
|
}
|
||||||
return c > 0
|
return !b
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDurationSort(asc bool, c1, c2 string) (bool, bool) {
|
func isDurationSort(asc bool, c1, c2 string) (bool, bool) {
|
||||||
|
|
@ -118,18 +118,6 @@ func isMetric(s string) (string, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDuration(s string) (time.Duration, bool) {
|
func isDuration(s string) (time.Duration, bool) {
|
||||||
// rx := regexp.MustCompile(`(\d+)([h|d])`)
|
|
||||||
// mm := rx.FindStringSubmatch(s)
|
|
||||||
// if len(mm) == 3 {
|
|
||||||
// n, _ := strconv.Atoi(mm[1])
|
|
||||||
// switch mm[2] {
|
|
||||||
// case "d":
|
|
||||||
// s = fmt.Sprintf("%dh", n*24)
|
|
||||||
// case "y":
|
|
||||||
// s = fmt.Sprintf("%dh", n*365*24)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
d, err := time.ParseDuration(s)
|
d, err := time.ParseDuration(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d, false
|
return d, false
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,8 @@ func TestGroupSort(t *testing.T) {
|
||||||
{true, []string{"95d", "93d"}, []string{"93d", "95d"}},
|
{true, []string{"95d", "93d"}, []string{"93d", "95d"}},
|
||||||
{true, []string{"1h10m", "59m"}, []string{"59m", "1h10m"}},
|
{true, []string{"1h10m", "59m"}, []string{"59m", "1h10m"}},
|
||||||
{true, []string{"95m", "1h30m"}, []string{"1h30m", "95m"}},
|
{true, []string{"95m", "1h30m"}, []string{"1h30m", "95m"}},
|
||||||
|
{true, []string{"b-21", "b-2"}, []string{"b-2", "b-21"}},
|
||||||
|
{false, []string{"b-21", "b-2"}, []string{"b-21", "b-2"}},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range uu {
|
for _, u := range uu {
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ func newTableView(app *appView, title string) *tableView {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *tableView) bindKeys() {
|
func (v *tableView) bindKeys() {
|
||||||
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, true)
|
v.actions[KeyShiftI] = newKeyAction("Invert", v.sortInvertCmd, false)
|
||||||
v.actions[KeyShiftN] = newKeyAction("Sort Name", v.sortColCmd(0), true)
|
v.actions[KeyShiftN] = newKeyAction("Sort Name", v.sortColCmd(0), true)
|
||||||
v.actions[KeyShiftA] = newKeyAction("Sort Age", v.sortColCmd(-1), true)
|
v.actions[KeyShiftA] = newKeyAction("Sort Age", v.sortColCmd(-1), true)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue